Vue dynamic v-model within v-for - javascript

I have the following fieldsets containing checkboxes:
<fieldset>
<label v-for="(count, value) in availableFilters.level"><input type="checkbox" data-filterName="level" :value="value" v-model="level" #change="(e) => handleCheckbox(e, 'level')"> {{value}} ({{count}})</label>
</fieldset>
<fieldset>
<label v-for="(count, value) in availableFilters.subject"><input type="checkbox" data-filterName="subject" :value="value" v-model="subject" #change="(e) => handleCheckbox(e, 'subject')"> {{value}} ({{count}})</label>
</fieldset>
<fieldset>
<label v-for="(count, value) in availableFilters.delivery"><input type="checkbox" data-filterName="delivery" :value="value" v-model="name" #change="(e) => handleCheckbox(e, 'delivery')"> {{value}} ({{count}})</label>
</fieldset>
Notice there's a bit of repetition here, but it works. Here's my Vue instance:
var vm = new Vue({
el: '#app',
data: {
level: [],
subject: [],
delivery: [],
availableFilters: {
level: {
"UG": 12,
"PG": 12,
}
}
},
...
I want something more like this so I don't have to repeat the same block over again:
<fieldset v-for="(filters, name) in availableFilters">
<label v-for="(count, value) in filters">
<input type="checkbox" :data-filterName="name" :value="value" v-model="name" #change="(e) => handleCheckbox(e, name, value)"> {{value}} ({{count}})
</label>
</fieldset>
However, this doesn't work and it seems that the v-model is not bound to the data property. How do I correctly pass that now? The data property name will be whatever name is.

To with situation you should put that properties (level, subject, delivery) inside an object called selected as follows :
selected: {
level: [],
subject: [],
delivery: []
}
and you should loop using v-for like :
<fieldset v-for="(filters, key,index) in availableFilters">
where the filters represents the value, key represents the key like level and ìndex represents the index such 0, using the key item we could access selected like selected[key] so we could bind the checkbox to that property easily.
Full example
new Vue({
el: '#app',
data() {
return {
selected: {
level: [],
subject: [],
delivery: []
},
availableFilters: {
level: {
"UG": 12,
"PG": 12,
},
subject: {
}
}
}
}
});
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app" class="container">
<fieldset v-for="(filters, key,index) in availableFilters">
<label v-for="(count, value) in filters">
<input type="checkbox" :data-filterName="this[filters]" :value="value" v-model="selected[key]" #change="onchange"> {{value}} ({{count}})
</label>
</fieldset>
<pre>{{selected}}</pre>
</div>

Related

vuejs filter array with checkboxes

I am using vuejs and I want to filter my array using checkboxes. I have tried using v-model to filter an array based on three options. If the name contains "Truck", "Van", or "Tx". I am not having any luck. Also i don't want to use a computed method.
new Vue({
el: "#app",
data: {
selection:[],
todos: [
{
"Name": "CHS_200_TL_L62_TRUCK"
},
{
"Name": "VHS_600_TL_L62_TRUCK"
},
{
"Name": "VHS_116_TL_L62_TRUCK"
},
{
"Name": "VHS_146_TX_L62_VAN"
},
{
"Name": "VHS_613_TL_L62_TRUCK"
},
{
"Name":"JE_50_OL_T62_TRUCK"
},
{
"Name": "VHS_T10_OL_L62_TRUCK"
},
{
"Name":"JE_59_OL_T62_TRUCK"
},
{
"Name": "LEE_100_TL_L62_TRUCK"
}
],
mounted:function(){
},
methods: {
}
},
methods: {
fin: function(todo){
todo
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<h2>Todos:</h2>
<input type="checkbox" id="vehicle1" name="vehicle1" value="Truck" v-model="checkedNames"> Truck<br>
<input type="checkbox" id="vehicle2" name="vehicle2" value="Van" v-model="checkedNames"> Van<br>
<input type="checkbox" id="vehicle1" name="vehicle1" value="TX" v-model="checkedNames">
<label for="vehicle1"> TX</label><br>
<li v-for="todo in todos">
{{todo.Name}}
</li>
</div>
If you don't want to use computed property , you can use watcher:
new Vue({
el: "#app",
data() {
return {
selection:[],
filtered: [],
todos: [{"Name": "CHS_200_TL_L62_TRUCK"}, {"Name": "VHS_600_TL_L62_TRUCK"}, {"Name": "VHS_116_TL_L62_TRUCK"}, {"Name": "VHS_146_TX_L62_VAN"}, {"Name": "VHS_613_TL_L62_TRUCK"}, {"Name":"JE_50_OL_T62_TRUCK"}, {"Name": "VHS_T10_OL_L62_TRUCK"}, {"Name":"JE_59_OL_T62_TRUCK"}, {"Name": "LEE_100_TL_L62_TRUCK"}],
}
},
watch: {
selection: {
handler() {
this.filtered= []
if (this.selection.length) {
this.todos.forEach(t => {
this.selection.forEach(s => {
if (t.Name.split('_').find(w => w === s.toUpperCase())) {
if (!this.filtered.find(f => f.Name === t.Name)) this.filtered = [ ...this.filtered, t]
}
})
})
} else {
this.filtered = [...this.todos]
}
},
deep: true,
immediate: true
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h2>Todos:</h2>
<input type="checkbox" id="vehicle1" name="vehicle1" value="Truck" v-model="selection"> Truck<br>
<input type="checkbox" id="vehicle2" name="vehicle2" value="Van" v-model="selection"> Van<br>
<input type="checkbox" id="vehicle1" name="vehicle1" value="TX" v-model="selection"> TX<br>
<ul>
<li v-for="todo in filtered">
{{todo.Name}}
</li>
</ul>
</div>
First of all, you have typos in your code :
In the script you've defined selection:[] but in the template you have used checkedNames which is undefined
To respond on your question, you should use a computed value that searchs for todos that contains selection
In your script define a computed value named todosFiltered
...
computed: {
todosFiltered() {
return this.todos.filter((todo) => {
return this.selection.every((select) => todo.Name.toLowerCase().includes(select.toLowerCase()));
});
},
},
....
In your template use todosFiltered instead of todos
<template>
<div id="app">
<h2>Todos:</h2>
<input
type="checkbox"
id="vehicle1"
name="vehicle1"
value="Truck"
v-model="selection"
/>
Truck<br />
<input
type="checkbox"
id="vehicle2"
name="vehicle2"
value="Van"
v-model="selection"
/>
Van<br />
<input
type="checkbox"
id="vehicle1"
name="vehicle1"
value="TX"
v-model="selection"
/>
<label for="vehicle1"> TX</label><br />
<li v-for="todo in todosFiltered">
{{ todo.Name }}
</li>
</div>
</template>

Vue.js post checkbox empty data or full data

I am trying to make a checkbox with vue js, so that the method is not written. I want the checkbox to be false in the first place, the next step is that when the checkbox is posted, I want "opening_balance" to be sent as an empty array.
or vice versa, if the checkbox is checked, I want it to appear in "opening_balance" when the data is posted.
For example:
POST
No Checkbox
opening_balance: {}
POST
Checked
opening_balance: {date: "2021-08-30", net_total: "1000.00", currency: "USD"}
new Vue({
el: '#app',
data() {
return {
attributes: {
opening_balance: {
net_total: 0,
date: new Date(),
currency: USD,
},
}
}
}
});
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.js"></script>
<div id="app" style="padding: 1rem;">
<div class="d-flex">
<input class="form-check-input" type="checkbox" value="false" #click="attributes.opening_balance" v-model="attributes.opening_balance">
<label class="form-check-label"><b> opening balance</b></label>
</div>
<div v-if="attributes.opening_balance">
<input type="text" id="Detaylari" class="form-control" v-model="attributes.opening_balance.date">
<input type="text" class="form-control" v-model="attributes.opening_balance.net_total">
<input type="text" class="form-control" v-model="attributes.opening_balance.currency">
</div>
</div>
If i understand you correctly please look at snippet bellow:
const app = new Vue({
el: '#app',
data() {
return {
attributes: {
opening_balance: {
},
}
}
},
methods: {
setBalance(e) {
if(!e.target.checked) {
this.attributes.opening_balance= {}
}
}
}
});
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" style="padding: 1rem;">
<div class="d-flex">
<input class="form-check-input" type="checkbox" #click="setBalance">
<label class="form-check-label"><b> opening balance</b></label>
</div>
<div v-if="attributes.opening_balance">
<input type="text" id="Detaylari" class="form-control" v-model="attributes.opening_balance.date">
<input type="text" class="form-control" v-model="attributes.opening_balance.net_total">
<input type="text" class="form-control" v-model="attributes.opening_balance.currency">
<p>{{ attributes.opening_balance }}</p>
</div>
</div>
You can use a watcher to set a empty object when checked is false.
new Vue({
el: '#app',
watch: {
isPosted: function(val) {
// on posted unchecked set empty object
if (!val) {
this.attributes.opening_balance = {};
} else {
this.$set(this.attributes.opening_balance, "date", "2021-08-30");
this.$set(this.attributes.opening_balance, "net_total", "1000.00");
this.$set(this.attributes.opening_balance, "currency", "USD");
}
},
},
data() {
return {
isPosted: false,
attributes: {
opening_balance: {},
}
}
}
});
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.js"></script>
<div id="app" style="padding: 1rem;">
<div class="d-flex">
<input class="form-check-input" type="checkbox" v-model="isPosted">
<label class="form-check-label"><b> opening balance</b></label>
</div>
<div v-if="isPosted">
<input type="text" id="Detaylari" class="form-control" v-model="attributes.opening_balance.date">
<input type="text" class="form-control" v-model="attributes.opening_balance.net_total">
<input type="text" class="form-control" v-model="attributes.opening_balance.currency">
</div>
</div>

How to check all checkboxes on selecting one in Vuejs?

isCheckAll: false,
methods: {
checkAll: function(){
this.isCheckAll = !this.isCheckAll;
},
updateCheckall: function(){
this.isCheckAll = true;
},
<div class="checkbox-alignment-form-filter">
<input type="checkbox" id="three" class="vh-product" #click='checkAll()' v-model='isCheckAll' />
<label class="productlist-specific" for="three"
> : 2011</label
>
</div>
<div class="checkbox-alignment-form-filter2">
<input type="checkbox" id="four" class="vh-product" #change='updateCheckall()'/>
<label class="productlist-specific" for="four">E0A</label>
</div>
<div class="checkbox-alignment-form-filter3">
<input type="checkbox" id="five" class="vh-product" #change='updateCheckall()'/>
<label class="productlist-specific" for="five">BR</label>
</div>
<div class="checkbox-alignment-form-filter4">
<input type="checkbox" id="six" class="vh-product" #change='updateCheckall()'/>
<label class="productlist-specific" for="six">E11350A</label>
</div>
<div class="checkbox-alignment-form-filter5">
<input type="checkbox" id="seven" class="vh-product" #change='updateCheckall()'>
<label class="productlist-specific" for="seven">E0BR</label>
</div>
How to select check and uncheck all checkboxes, on selecting one checkbox.
I don't know what stopping me to proceed, For every checkbox taken one #click and sub checkboxes also taken one #click. and then in js i am toggling that condition to check and uncheck.
I think you had some problems with your logic, so let me explain this for you:
You need to have a v-model on all of them, to be able to control state updates (whenever a user clicks something it needs to be updated).
You don't need to have two methods for all of this, you can have just one to enable all of the checkboxes. The rest can be handled by v-model.
The following code will enable/disable all of the checkboxes by clicking the first one, and if you click the other ones, it'll just enable/disable that particular checkbox.
<div id="app">
<input type="checkbox" v-model="checkboxes[0]" v-on:click="checkAll()">
<input type="checkbox" v-model="checkboxes[1]">
<input type="checkbox" v-model="checkboxes[2]">
<input type="checkbox" v-model="checkboxes[3]">
</div>
const app = new Vue({
el: "#app",
data: {
// control the state of checkboxes
checkboxes: [false, false, false, false]
},
methods: {
checkAll: function(){
for(let i = 1; i < this.checkboxes.length; i++){
// update all of the checkboxes to the value of the first one.
this.checkboxes[i] = !this.checkboxes[0];
}
}
}
})
your problem was your data handling.
i made you a playable example in CodeSandbox.
with this you are able to control the main checkbox bidirectional.
if all sub checkboxes are true the main checkbox will be set to true as well.
<template>
<div id="app">
<div class="checkbox-group">
<div class="main-checkbox">
<input
type="checkbox"
:name="checkboxes.main.label"
v-model="checkboxes.main.value"
#input="toggleAllCheckboxes()"
/>
<label :for="checkboxes.main.label">{{ checkboxes.main.label }}</label>
</div>
<div
class="sub-checkbox"
v-for="(checkbox, index) in checkboxes.sub"
:key="index"
>
<input
type="checkbox"
:name="checkbox.label"
v-model="checkbox.value"
/>
<label :for="checkbox.label">{{ checkbox.label }}</label>
</div>
</div>
</div>
</template>
<script>
export default {
name: "App",
data: () => ({
checkboxes: {
main: {
value: false,
label: ":2011",
},
sub: [
{
value: false,
label: "E0A",
},
{
value: false,
label: "BR",
},
{
value: false,
label: "E11350A",
},
{
value: false,
label: "E0BR",
},
],
},
}),
computed: {
mainIsChecked: function () {
return this.checkboxes.main.value;
},
allSubsAreChecked: function () {
return this.checkboxes.sub.every((checkbox) => checkbox.value);
},
},
watch: {
allSubsAreChecked: function () {
this.checkboxes.main.value = this.allSubsAreChecked;
},
},
methods: {
toggleAllCheckboxes() {
for (const checkbox of this.checkboxes.sub) {
checkbox.value = !this.mainIsChecked;
}
},
},
};
</script>
If you are working with multiple checkboxes, you should bind them to the same Array using v-model according to the Vue docs. https://v2.vuejs.org/v2/guide/forms.html#Checkbox
In your case, this means that you should have one array managing all the checkboxes except the one which controls selecting them all, that should be handled differently.
Each of those checkboxes should have a value binding as well, telling Vue which value they represent in the array.
You could also store all the possible checkbox values in an array which would help you reducing code duplication in your template.
You can render all 4 checkboxes which can be separately checked using a v-for loop, and manage them with the same v-model.
The checkbox controlling the "all checked" state should be managed with a different v-model. When that checkboxes value changes, we should check/uncheck all the other checkboxes, which can be done easily using a watcher.
Here's an example using this approach https://codesandbox.io/s/inspiring-dijkstra-fzkmn?file=/src/App.vue
export default {
name: "App",
data: () => ({
selectedValues: [],
allChecked: false,
}),
computed: {
isAllChecked() {
return false;
},
allCheckboxOptions() {
return [
{
label: "E0A",
value: "four",
},
{
label: "BR",
value: "five",
},
{
label: "E11350A",
value: "six",
},
{
label: "E0BR",
value: "seven",
},
];
},
},
methods: {
checkAll() {
this.selectedValues = this.allCheckboxOptions.map(
(option) => option.value
);
},
checkNone() {
this.selectedValues = [];
},
},
watch: {
allChecked(isAllChecked) {
if (isAllChecked) {
this.checkAll();
} else {
this.checkNone();
}
},
},
};
<template>
<div id="app">
<div class="checkbox-alignment-form-filter">
<input
type="checkbox"
id="three"
class="vh-product"
v-model="allChecked"
/>
<label class="productlist-specific" for="three"> : 2011</label>
</div>
<template v-for="(checkboxOption, index) in allCheckboxOptions">
<div
:key="checkboxOption.value"
:class="`checkbox-alignment-form-filter${index + 2}`"
>
<input
type="checkbox"
:id="checkboxOption.value"
:value="checkboxOption.value"
v-model="selectedValues"
class="vh-product"
/>
<label class="productlist-specific" for="four">
{{ checkboxOption.label }}
</label>
</div>
</template>
checked: {{ selectedValues }}
</div>
</template>

#click on checkbox add/remove data

I currently have the following scenario:
I have multiple checkboxes, once any is clicked, the value of it will get added to an array. If the checkbox is unchecked then the item needs to be removed out of the array again.
selectAddOn(addOnId) {
if (! this.selectedAddOns.includes(addOnId)) {
this.selectedAddOns.push(addOnId);
}
}
The following works and it adds them to my selectedAddOns[]. But when the checkbox is checked again, it is not removed. Sure, I could just use else, but...
Unfortunately, the browser behavior is when you click on a <label>, a click event will automatically be triggered on the <input>, so the outer div receives 2 events, one from label, one from input. I am aware that I can work around this by adding #click.prevent on the <label>, but this then will not add my custom checkbox styles.
<div #click="selectAddOn(index)" class="col-6" v-for="(addOn, index) in categories[categoryId].addOns">
<label class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input">
<span class="custom-control-indicator"></span>
<span class="custom-control-description">{{ addOn.name }} (+ ${{ addOn.price }})</span>
</label>
</div>
Any idea on how I can work around this scenario?
This is a built-in behavior of v-model when used with an array on multiple checkboxes. You don't need a click handler. (Code shamelessly lifted from Bert's answer.)
console.clear()
new Vue({
el: "#app",
data:{
selectedAddOns:[],
categories:[
{
addOns:[
{name: "AddOn One", price: 10},
{name: "AddOn two", price: 20},
{name: "AddOn Three", price: 30},
]
},
],
categoryId: 0
}
})
<script src="https://unpkg.com/vue#2.4.2"></script>
<div id="app">
Selected Addons: {{selectedAddOns}}
<div class="col-6" v-for="addOn, index in categories[categoryId].addOns">
<label class="custom-control custom-checkbox" >
<input type="checkbox" class="custom-control-input" :value="index" v-model="selectedAddOns" >
<span class="custom-control-indicator"></span>
<span class="custom-control-description">{{ addOn.name }} (+ ${{ addOn.price }})</span>
</label>
</div>
</div>
Put the click event handler on the input.
console.clear()
new Vue({
el: "#app",
data:{
selectedAddOns:[],
categories:[
{
addOns:[
{name: "AddOn One", price: 10},
{name: "AddOn two", price: 20},
{name: "AddOn Three", price: 30},
]
},
],
categoryId: 0
},
methods:{
selectAddOn(addOnId) {
let index = this.selectedAddOns.findIndex(a => a === addOnId)
if (index >= 0)
this.selectedAddOns.splice(index, 1)
else
this.selectedAddOns.push(addOnId);
}
}
})
<script src="https://unpkg.com/vue#2.4.2"></script>
<div id="app">
Selected Addons: {{selectedAddOns}}
<div class="col-6" v-for="(addOn, index) in categories[categoryId].addOns">
<label class="custom-control custom-checkbox" >
<input type="checkbox" class="custom-control-input" #click="selectAddOn(index)" >
<span class="custom-control-indicator"></span>
<span class="custom-control-description">{{ addOn.name }} (+ ${{ addOn.price }})</span>
</label>
</div>
</div>

Update parent class in Vue.js

I'm a Vue.js noob and trying to figure out how to toggle a class inside a for loop for the parent of the link that is clicked.
I want to click on the "Advanced" link and then toggle a class of overlay for the section and .advanced-fields elements only in that section. I know I can add the onOff data attribute to each section but I am saving to a database and don't want to add in unnecessary data fields.
Can anyone point me in the right direction. Thanks.
Here is a simplified version of my code.
<div id="app">
<section v-bind:class="{ overlay: onOff }" v-for="section in sections">
Advanced
<div class="advanced-fields" v-bind:class="{ overlay: onOff }" v-show="onOff">
<fieldset>
<label>
ID
<input type="text" name="section[id]">
</label>
</fieldset>
<fieldset>
<label>
Class
<input type="text" name="section[css_class]">
</label>
</fieldset>
</div>
</section>
</div>
<script>
new Vue({
el: "#app",
data:{
"sections": [
{
"id": "section-1",
"css_class": ""
},
{
"id": "section-2",
"css_class": ''
}
],
"onOff": false
},
methods: {
showAdvanced: function(section) {
this.onOff = !this.onOff;
}
}
});
</script>
This answer Vue bind click that adds active class (and removes from the last one) helped me figure it out.
https://jsfiddle.net/ferne97/n1hfde94/
Here is the updated code that works properly.
<div id="app">
<section v-bind:class="{ overlay: index == isActive }" v-for="(section, index) in sections">
Advanced
<div class="advanced-fields" v-bind:class="{ overlay: index == isActive }" v-show="index == isActive">
<fieldset>
<label>
ID
<input type="text" name="section[id]" v-model="section.id">
</label>
</fieldset>
<fieldset>
<label>
Class
<input type="text" name="section[css_class]" v-model="section.css_class">
</label>
</fieldset>
</div>
</section>
<pre>{{ $data }}</pre>
</div>
<script>
new Vue({
el: "#app",
data:{
"sections": [
{
"id": "section-1",
"css_class": ""
},
{
"id": "section-2",
"css_class": ''
}
],
"isActive": null
},
methods: {
showAdvanced: function(index) {
this.isActive = this.isActive === index ? null : index
}
}
});
</script>

Categories

Resources