Basically I have a dropdown in which each option has an attribute corresponding to the ID of an image. My goal is to go to that image when an option is chosen. For that I am trying to use:
const myCustomNext = () => {
flkty.selectCell(somevar)
};
somevar is initially set to #someid, when I click my button, it goes to the cell with that ID perfectly.
The issue starts once I update the value of somevar. As soon as I do and then click the button, I get the following Error:
"Cannot read property 'selectCell' of null"
I logged both the initital somevar and the updated one. Other than the ID itself, they are absolutely identical so I have no clue where I am going wrong. I tried switchen the static and reloadOnUpdate settings but that didn't help.
Here a more complete example that might show better what I am trying to do:
const FlickTest = () => {
const [varimg, setVarimg] = useState("#cG9zdDoxNA");
let flkty = null;
function setVariant(e) {
let index = e.target.selectedIndex;
let optionElement = e.target.childNodes[index]
let option = optionElement.getAttribute('data-imgid');
setVarimg(`#${option}`);
}
const myCustomNext = () => {
flkty.selectCell(varimg)
};
return (
<>
<button onClick={myCustomNext}>My custom next button</button>
<select onChange={setVariant}>
{variants.map((variant) =>
<option data-imgid={variant.gallerie[0].id} value={variant.farbe} key={variant.farbe}>{variant.farbe}</option>
)}
</select>
<Flickity
flickityRef={c => (flkty = c)}
className={'carousel'}
elementType={'div'}
options={flickityOptions}
disableImagesLoaded={true}
reloadOnUpdate={true}
static={true}
>
{variants.map((galitem) =>
galitem.gallerie.map((galimg) =>
<div key={galimg.id} id={galimg.id.replace(/[^\w\s]/gi, '')}>
<span>{galimg.id}</span>
<Image fluid={galimg.localFile.childImageSharp.fluid} />
</div>
)
)}
</Flickity>
</>
)
}
Any ideas or pointers would be much appreciated :)
Switched from a dropdown to buttons just to simplify the whole thing and see where it goes wrong. Seems like flickity only accepts the value directly but not from state or any other variable.
This works:
const selectSlide = (e) => {
flkty.selectCell( `.${e.target.getAttribute("data-selector")}` )
};
...
<button onClick={selectSlide} data-selector={variant.gallerie[0].id} key={variant.farbe}>{variant.farbe}</button>
If anybody knows if this is a flickity thing or (more likely) I was doing something completely wrong I'd still appreciate some pointers so I know better next time :)
Related
What I need
Let's start with The mentions plugin taken from the docs.
I would like to enhance if with the following functionality:
Whenever I click on an existing MentionNode, the menu gets rendered (like it does when menuRenderFunction gets called), with the full list of options, regardless of queryString matching
Selecting an option from menu replaces said mention with the newly selected one
Is there a way to implement this while leaving LexicalTypeaheadMenuPlugin in control of the menu?
Thank you for your time 🙏🏻
What I've tried
I figured that maybe I could achieve my desired behaviour simply by returning the right QueryMatch from triggerFn. Something like this:
const x: FC = () => {
const nodeAtSelection = useNodeAtSelection() // Returns LexicalNode at selection
return (
<LexicalTypeaheadMenuPlugin<VariableTypeaheadOption>
triggerFn={(text, editor) => {
if ($isMentionsNode(nodeAtSelection)) {
// No idea how to implement `getQueryMatchForMentionsNode`,
// or whether it's even possible
return getQueryMatchForMentionsNode(nodeAtSelection, text, editor)
}
return checkForVariableBeforeCaret(text, editor)
}}
/>
)
}
I played around with it for about half an hour, unfortunately I couldn't really find any documentation for triggerFn or QueryMatch, and haven't really made any progress just by messing around.
I also thought of a potential solution the I think would work, but feels very hacky and I would prefer not to use it. I'll post it as an answer.
So here is my "dirty" solution that should work, but feels very hacky:
I could basically take the function which I provide to menuRenderFn prop and call it manually.
Let's say I render the plugin like this:
const menuRenderer = (
anchorElementRef,
{ selectedIndex, selectOptionAndCleanUp, setHighlightedIndex }
) => { /* ... */}
return (
<LexicalTypeaheadMenuPlugin menuRenderFn={menuRenderer} /* ... other props */ />
)
I could then create a parallel environment for rendering menuRenderer, something like this:
const useParallelMenu = (
menuRenderer: MenuRenderFn<any>,
allOptions: TypeaheadOption[],
queryString: string
) => {
// I could get anchor element:
// 1. either by using document.querySelector("." + anchorClassName)
// 2. or by extracting it from inside `menuRenderFn`:
// menuRenderFn={(...params) => {
// extractedRef.current = params[0].current;
// return menuRenderer(...params)
// }}
const anchorEl = x
const [selectedIndex, setHighlightedIndex] = useState(0)
const nodeAtSelection = useNodeAtSelection() // Returns LexicalNode at selection
const selectOptionAndCleanUp = (option: TypeaheadOption) => {
// Replace nodeAtSelection with new MentionsNode from `option`
}
return () =>
$isMentionsNode(nodeAtSelection) &&
menuRenderer(
anchorEl,
{
selectedIndex,
setHighlightedIndex,
selectOptionAndCleanUp,
options: allOptions
},
queryString
)
}
On paper, this seems like a viable approach to me... but I would really prefer not to have to do this and instead let LexicalTypeaheadMenuPlugin manage the state of my menu, as it is intended to do.
im pretty new at coding, currently studing front end dev. I´m on my 5:th week learning JS and got a challange to create a toDo list with Typescript.
When i create a task in my app it has a checkbox and a "trash"-button. The idéa of the trash button is pretty clear and the checkbox is going put the task last in the list when its "checked".
I noticed some repetative code while creating my event listeners. So I found a way to add the event listener to multiple elements but I can't wrap my head around how to call the different functions that corresponds to what was clicked.
this is how I found a way to add the event listener to each element. But from here, how can I call a function based on what was clicked?
document.querySelectorAll('.add-click-listener').forEach(item => {
item.addEventListener('click', event => {
})
})
this was the code from the beginning
let checkbox = Array.from(document.querySelectorAll('.hidden-checkbox'));
checkbox.forEach((item) => {
item.addEventListener('click', checkboxStatusShift);
});
let trashBtn = Array.from(document.querySelectorAll('.trash-btn'));
trashBtn.forEach((item) => {
item.addEventListener('click', deleteTask);
});
this will be the function to "trash" a task:
function deleteTask(event: any) {
const index = (event.currentTarget);
const buttonId = index.id?.replace('remove-', '');
const currentTask = todoDatabase.filter((object) => object.id === buttonId)[0];
const currentTaskId = currentTask.id;
console.log(buttonId);
console.log(currentTaskId);
if (buttonId == currentTaskId) {
todoDatabase.splice(0, 1);
printTodoList();
}
}
I haven't started the code for the checkbox function yet.
Very grateful for any tips I can get.
Thanks for all the replies, this pointed me in the right direction and I got it working as intended, the following code was the result:
document.querySelectorAll('.add-click-listener').forEach(item => {
item.addEventListener('click', (event: any) => {
const thisWasClicked = event.currentTarget;
let trashBtn = Array.from(document.querySelectorAll('.trash-btn'));
const filterTrashBtn: any = trashBtn.find((btn) => btn.id === thisWasClicked.id);
const findTask = trashBtn.indexOf(filterTrashBtn);
if (filterTrashBtn?.id == thisWasClicked.id) {
todoDatabase.splice(findTask, 1);
printTodoList();
}
});
});
Now I can just write the code for my checkboxes with another if and some variables etc.
From my experience, you can avoid repeating code by using an event delegation pattern. You have to attach an event listener to the parent element, and then inside the handler to check if it matches your child element. The event by clicking on child will bubble so you will catch it. In code it will look like this:
document.querySelector('#parent-element').addEventListener('click', function(e) {
if (e.target && e.target.matches('.child-element')) {
// do your stuff here
}
});
You can bind to the root element where the checkboxes are located and handle the target element that fired the event.
If the HTML looks like this:
<div id="app">
<div class="checkboxes">
<input id="1" type="checkbox" /><label for="1">Checkbox 1</label>
<input id="2" type="checkbox" /><label for="2">Checkbox 2</label>
<input id="3" type="checkbox" /><label for="3">Checkbox 3</label>
</div>
</div>
An example JS code could be as shown below:
const root = document.getElementsByClassName('checkboxes')[0];
root.addEventListener('click', function (event) {
const target = event.target;
if (target.tagName == 'INPUT') {
console.log(target.id);
console.log(target.checked);
}
});
Bind a click event to the root div with the class name "checkboxes" and handle who exactly fires the event.
If you can use Jquery then the same can be done with on function
https://api.jquery.com/on
$(div.checkboxes).on("click", ".checkbox", function(event){})
I've got a 'list' component, which allows you to add an item to a state array, and then display it from the state afterwards. These list items can then be removed by the user afterwards (or should be able to).
There's four state props in this component:
currentList: content in the input that's to be added to the list array
setCurrentList: what's used to change the content in the input
fullList: the full array list
setFullList: used to add the currentList content to the array, and removed
I'm using .filter() to create a copy of the state array, and then set the state afterwards in this function:
const deleteFromList = (e) => {
console.log("Delete button pressed")
console.log(e)
let fullList = props.fullListState
let setFullList = props.setFullListState
let filteredArray = fullList.filter(item => item)
setFullList(filteredArray)
}
However, every time I execute this function (i.e. when the delete button is pressed), it just creates a loop and the first two console.logs are just repeatedly done.
This is the full return function itself:
<>
<label className="setup-jobs-label">{props.label}</label>
<div className="setup-jobs-input-container">
<input className="setup-jobs-alt-input" type="text" onChange={onChange} value={props.currentListState} />
<button className="setup-jobs-add-button" onClick={addToList}>Add</button>
</div>
{ props.fullListState === [] ? null : props.fullListState.map(x => {
return <div className="setup-jobs-input-container" key={props.fullListState[x]}>
<p className="setup-jobs-input-paragraph">{x}</p>
<button className="setup-jobs-delete-button" onClick={deleteFromList(x)}>Delete</button>
</div>
}) }
</>
The important bit is the bottom conditional render, which checks to see if the state array is empty, and if so, not display anything. If it isn't, then it returns null.
Any advice would be appreciated - not sure what I'm doing wrong in the filter function.
In your onClick handler, you pass the result of the execution of deleteFromList, you should pass a reference to this function instead :
// note the '() =>'
<button className="setup-jobs-delete-button" onClick={() => deleteFromList(x)}>Delete</button>
See https://reactjs.org/docs/handling-events.html for more details about this.
Beside this, your filter logic does not seem right :
// this line only removes falsy values, but not the "e" values
let filteredArray = fullList.filter(item => item)
// you should implement something like this
let filteredArray = fullList.filter(item => [item is not "e"])
// this should work as we work on objects references
let filteredArray = fullList.filter(item => item !== e)
I am trying to display calculated values from a function called renderAnalysis - all which works until the bottom of the function where I am trying to return a div with the values that are found. However, this isn't displaying (all values are returned though in the console when I test it out) and was wondering what I need to adjust to prevent the values I can show in the console? I think I'm doing a render call wrong or something... I've added the code for reference. Thank you!
renderAnalysis = () => {
let reactions = this.state.reactions;
let foods = Object.keys(reactions);
foods.map((food) => {
let reactionDescriptions = reactions[food];
let reactionDescriptionKeys = Object.keys(reactionDescriptions);
let responseKey = Object.keys(reactionDescriptions);
responseKey.map((singleDescription) => {
let value = reactionDescriptions[singleDescription];
// RETURN VALUE NOT SHOWING
return <div> {(food, singleDescription, value)} </div>;
});
});
};
Further information: There is a toggle I made in state that if someone clicks a button it triggers this function. Basically
<button onClick={this.displayRenderAnalysisToggle}>
Render Analysis
</button>{" "}
<br />
{this.state.displayRenderAnalysisToggle ? this.renderAnalysis() : null}
</div>
I'm struggling with reactjs for no reason. I'm a little confused about the magic behind and I'm not able to perform a simple operation of adding object / removing object from an array and display it.
I my parent, I have a method which on click append a new element:
appendNewPma(){
var newPma = this.state.pma.slice();
newPma.push(PmaType1);
this.setState({pma:newPma})
}
then my render method is like that:
render() {
return (
<div>
<a className="waves-effect waves-light btn" onClick={this.appendNewPma}>new</a>
{this.state.pma.map((Item, index) => (
<Item
key = {index}
ref = {"pma" + index.toString()}
onDelete = {() => this.onDelete(index)}
title = {index}/>
))}
</div>
);
}
Append work fine, but my array doesn't contain an object to display but rather a magical function that I don't understand.
But when I try to delete an object:
onDelete(idx){
console.log(idx);
var pma = this.state.pma.slice();
pma.splice(idx, 1);
this.setState({pma:pma})
}
When I delete from the array, no matter what index I will remove, it will only remove the last object. I know my code is not ok, but I have no idea how you can render element for an array of object (here my array is list of function constructor).
It will work better if I could get a straight ref to my object. Of course, I tryed to removed from the ReactDom, but was complening I was not updating from the parent...
I just want a simple array push/pop pattern with update.
Thanks for your help
Try below code. hope so it solve your issue.
addToArray = (event) => {
this.state.pma.push({"name ": "xyz"});
this.setState(
this.state
)
}
removeFromArray =(index) => {
var updatedArr = this.state.pma.splice(index, 1);
this.setState({arr : updatedArr})
}