I have a series of text fields with a button next to each field. When the user taps the button next to one of the fields I want to apply a particular style to that button (to change its colour) - essentially allowing the user to "tick" that they have checked that field (similar to a checklist).
There are nine text fields/buttons on the page, and I have the status of all the buttons stored in an array called items_checked which is initialized in data() as the following:
items_checked: [false, false, false, false, false, false, false, false, false]
Each button has the following code:
<button class="btn btn-danger" v-on:click="toggleChecked(0)" v-bind:class="{'itemChecked' : items_checked[0]}">
where the number indicates the index of the button (i.e. 0 is the first button, 1 is the second button, etc.) to correspond to the equivalent boolean in items_checked.
The v-on:click event just toggles the checked status in the items_checked for the tapped button:
toggleChecked (itemIndex) {
if (this.items_checked[itemIndex] === false) {
this.items_checked[itemIndex] = true
} else {
this.items_checked[itemIndex] = false
}
console.log(this.items_checked)
}
This works as the console.log shows the boolean values toggling.
However, the v-bind does not work as the itemChecked class does not get applied to the button. It seems to be an issue binding to a boolean value within an array, as when I bind just to a standard boolean declared within data() it works fine.
I do eventually need all the checked statuses stored in an array, so that I can evaluate that all have been checked before allowing the user to submit the page.
Any help would be appreciated.
This is common reactivity problem.
In document:
Due to limitations in JavaScript, Vue cannot detect the following
changes to an array:
When you directly set an item with the index, e.g.
vm.items[indexOfItem] = newValue
When you modify the length of the
array, e.g. vm.items.length = newLength
You can use deep copy
toggleChecked (itemIndex) {
this.items_checked[itemIndex] = !this.items_checked[itemIndex]
this.items_checked = JSON.parse(JSON.stringify(this.items_checked))
console.log(this.items_checked)
}
Another solution is using Vue.set
toggleChecked (itemIndex) {
var oldValue = this.items_checked[itemIndex]
this.$set(this.items_checked, itemIndex, !oldValue)
console.log(this.items_checked)
}
Related
I have a Vue 3 application
I have an object of posts. Each post has a different category. At the top of the page I have check boxes that I can check to filter the array and only show the categories I added to another array called visibleList. If I click the check box for cars then 'cars' is added to visibleList.
I need to show only posts with the category of cars if I check the cars checkbox. Now if I check the box I see no posts
I created an extra object that I can use to filter the objects. That object is populated when I load the app.
When I check a box the posts disappear. In the console I can see the number of posts with the correct category showing true and the others showing false.
Here is my function for sorting the posts:
function filterPosts() {
filtered.value = posts.value;
if (visibleList.value.length != 0) {
filtered.value = filtered.value.filter((e) => {
visibleList.value.includes(e.category);
console.log(visibleList.value.includes(e.category));
});
} else {
filtered.value = posts.value;
}
}
What am I doing wrong that I cannot see the posts from the selected category?
Looks like you are not returning a value from the filter function. The filter function expects a boolean value to determine whether an element should be included in the filtered array or not.
filtered.value = filtered.value.filter((e) => {
return visibleList.value.includes(e.category);
});
Just an Alternative!!
Since you are using the arrow function, you can use this approach to filter down the value.
filtered.value = filtered.value.filter((e) => visibleList.value.includes(e.category));
I have a series of checkboxes that need to push their name into a state array when checked, and remove their name when they are unchecked. No matter what I do, checking any sets the array to contain only that checkbox's name.
const [selectedItems, setSelectedItems] = useState([]);
useEffect(()=> {
// Log selectedItems out every time it changes.
console.log(selectedItems) // Always only the last selected checkbox name, no others.
}, [selectedItems])
const onCheckChange = e=> {
console.log(e.target.name, e.target.checked) // Are as expected, the name and value.
if (e.target.checked) {
setSelectedItems(prev=> [...prev, e.target.name])
// setSelectedItems([...selectedItems, e.target.name]) // Same result.
} else {
setSelectedItems( prev=> prev.filter(s=> s!==e.target.name) )
// setSelectedItems(selectedItems.filter(s=> s!==e.target.name)) // Same result.
}
}
Every time I check a checkbox, selectedItems is set to contain only the selected item's name. Every time I uncheck a checkbox, setSelectedItems is set to [].
This seems like the most normal pattern ever, but there must be some weirdness with arrays and useState? Thanks to anyone who can help!
The problem is in e.target.name. Most likely all your checkboxes are using the same name.
To fix, try to give different names, or use something else to find which checkbox was interacted (for example use index, with adding it as an argument of your onChange handler)
I'm working on a filter, which filters a array of nested arrays down to the value of one last index.
This happens in 5 steps. At each step you choose which index value (string) get used to filter the array further.
Example: You have 5 categories, each have 6 themes. Each of these 6 themes has 6 focusses(sub themes). Each focus has 6 questions. Each question has 1 answer. First you pick a categorie. This narrows the arrays down to all arrays with that categorie. Then a theme, which narrows the array down to only the arrays with that theme... etc...
So far I managed to filter down to the right question.
You can find the code here: https://github.com/okestens/vertical-filter.git
To get this work, I hardcoded the string "Deskundigheid" as a condition for the equality operator (===) that get used for the filter.
Example:
// FILTER QUESTIONS // I tried to use state.focus[0] but it does not work
let unique_questionsA = [. // now this is hardcoded
...new Set(formsA.filter((i) => i[2] === "Deskundigheid").map((i) => i[3])),
]; --------------
// FUNCTION
function displayQuestionsA() {
state.questions = [];
unique_questionsA.forEach(function (question, index) {
document.getElementById("question" + index).innerHTML = question;
state.questions.push(question);
});
------
// the state object
let state = {
category: [],
themes: [],
focus: [],
question: [],
answer: [],
};
But. What I want this filter to use is not a hardcoded string (deskundigheid) but the exact string that is visible in the div (coming from a click event which creates this filtered array and get stored in the state object). See image.
I thought: I need to track these arrays (with an object called 'state', capturing these dynamic arrays). If I then want to filter the right questions, by using the value (string) of the chosen focus (For example 'Deskundigheid', which is visible to the user), I just refer to the corresponding index value (state.focus[0]) of that chosen focus string, coming from the dynamic state object.
But, if I use the index state.focus[0] for the filter which creates the questions array, I get an empty array :(
My thought: Although the empty focus array (inside the state object), which get filled by a click event, eventually is filled with the right strings, the filter for the new array (unique_questionsA), which uses 'state.focus[0]' does not read the filled array as ‘filled’ but as empty.
I have not idea why :(
I hope I'm clear. If so, and you maybe have a clue, I would love to have a chat! Thanks O
The question can be summed up to
how do I get the text of the element when clicked, in an onclick event
listener callback function.
Your focusScript.js can be modified to
function displayQuestionsA(e) {
state.questions = [];
let unique_questionsA = [...new Set(formsA.filter((i) => i[2] === e.target.innerText).map((i) => i[3]))];
}
document.querySelector(".focus__A").addEventListener("click", displayQuestionsA);
Notice the e.target.innerText which contains the text inside the element that triggered the event(which you clicked).
if I got you correctly - both map and filter functions can give your callback second parameter - the index.
arr.map((n,i)=>{console.log(`[${i}]:${n}`)})
I have a bunch of check boxes and the value of each one is a different price. I created an object that holds the function to push the values into an array if the box is checked. When I console log the empty array it displays as empty so i know that works. I just cant get it to console log with the pushed value in it.
<input type="checkbox" id="bac" value ="1">bacon - $1
var allIngredients = {
ingredientArray: [],
baconBox: function() {
var bacon = document.getElementById('bac');
if (bacon.checked === true) {
this.ingredientArray.push(bacon.value);
}
},
edit:
console.log(allIngredients.ingredientArray);
that returns an empty array like this "[]". I cannot get it to return an array with the value of bacon in it like this "[1]" when i check the box. The value of bacon is 1.
As others have pointed out in the comments, you should call the function allIngredients.baconBox().
One option is to add an event listener, like so:
document.getElementById('bac').addEventListener('change', function() {
allIngredients.baconBox();
});
Although a downside of this approach is that unchecking the box wouldn't remove the bacon.value from your allIngredients array.
I have a list of checkboxes that execute functions on check and uncheck. I also have an observable array that holds the values of the checkboxes currently active (put into local storage). The relevant code is here:
this.layerToggleChecked = knockout.observableArray();
// ...
this.layerToggle = function (source, name, type, url, description) {
return knockout.computed({
read: function () {
return this.layerToggleChecked();
},
write: function (checked) {
if (checked) {
alert("loading");
this.layerToggleChecked.push(source());
} else {
alert("removing");
this.layerToggleChecked.remove(source());
}
}
}, this);
}
The checkboxes work as planned triggering the functions until I added return this.layerToggleChecked(); which returns
knockout-3.2.0.js:13 Uncaught TypeError: b.push is not a function
Knockout's checked binding handles arrays differently from other values. You're expecting a true or false to be written to the computed observable, but you're returning an array, which obviously aren't the same.
From http://knockoutjs.com/documentation/checked-binding.html:
Special consideration is given if your parameter resolves to an array. In this case, KO will set the element to be checked if the value matches an item in the array, and unchecked if it is not contained in the array.
When the user checks or unchecks the checkbox, KO will add or remove the value from the array accordingly.
Since you're using an array to hold the checked values, you're better off binding directly to it:
<input type="checkbox" data-bind="checkedValue: source, checked: layerToggleChecked" />