I have a checkboxlist, I am trying to implement SelectAll/DeselectAll functionality.The items of the checkboxlist are being bound from the database.
This is how my checkboxlist looks
<div class="options"
data-bind="foreach: Factor,visible: true" style="display: none;">
<label>
<input type="checkbox" class='roles' name='roles'
data-bind="attr: { value: Id },
checked:MyViewModel.MyData.MyCheckedValues" />
<span data-bind="text: Name"></span>
</label>
</div>
MyCheckedValues and Factor are observable arrays here.
This is how MyData looks
MyData: function () {
var currentObject = this;
currentObject.MyCheckedValues= ko.observableArray()
}
selectAll: function()
{
ko.utils.arrayForEach();
return true;
}
In the internet articles I found, a separate function is used where they declare an attribute called IsSelected and set it to false initially etc and then loop through it.
But I dont have any separate function related to this.
Can you help me implementing select/deselect all?
You can also use ko.computed for selectAll and Deselect.
html:-
<span data-bind="text: selectAll()?'Deselect All':'Select All'"></span><input type="checkbox" data-bind="checked: selectAll" />
<div class="options" data-bind="foreach: Factor,visible: true" style="display: none;">
<label>
<input type="checkbox" class='roles' name='roles' data-bind="attr: { value: id },checked:isSelected" /> <span data-bind="text: name"></span>
</label>
</div>
ViewModel:-
function Factor(id, name) {
this.id = id;
this.name = name;
this.isSelected = ko.observable(false);
}
function viewModel() {
var currentObject = this;
currentObject.Factor = ko.observableArray([new Factor(1, "Jack"), new Factor(2, "John")]);
currentObject.selectAll = ko.computed({
read: function () {
var item = ko.utils.arrayFirst(currentObject.Factor(), function (i) {
return !i.isSelected();
});
return item == null;
},
write: function (value) {
ko.utils.arrayForEach(currentObject.Factor(), function (i) {
i.isSelected(value);
});
}
});
}
ko.applyBindings(new viewModel());
Working Fiddle
Since I can't see your full model and am a bit confused by your bindings, I've made a quick example for you that you should be able to modify for your use.
Markup:
<button data-bind="click: selectAllCheckboxes">Select all</button>
<button data-bind="click: deselectAllCheckboxes">Deselect all</button>
<ul data-bind="foreach: data">
<li>
<label>
<input type="checkbox" data-bind="value: Id, checked: $parent.selectedCheckboxes">
<span data-bind="text: Name"></span>
</label>
</li>
</ul>
And the JS:
var myViewModel = function () {
this.data = ko.observableArray( [
{ Id: 1, Name: 'Example 1' },
{ Id: 2, Name: 'Example 2' },
{ Id: 3, Name: 'Example 3' },
{ Id: 4, Name: 'Example 4' },
{ Id: 5, Name: 'Example 5' }
] );
this.selectedCheckboxes = ko.observableArray();
this.selectAllCheckboxes = function () {
var selectedCheckboxesArray = this.selectedCheckboxes();
selectedCheckboxesArray.length = 0;
ko.utils.arrayForEach( this.data(), function ( data ) {
selectedCheckboxesArray.push( '' + data.Id );
} );
this.selectedCheckboxes.valueHasMutated();
};
this.deselectAllCheckboxes = function () {
this.selectedCheckboxes().length = 0;
this.selectedCheckboxes.valueHasMutated();
};
};
ko.applyBindings( new myViewModel() );
And here's a fiddle: http://jsfiddle.net/4VGGd/
Related
I have a const array of objects which set to the data property on created(), I am trying to update the object properties as the user entered the form, the Console print shows the value updated, but the DOM is changing. DOM is updated when the key value is changed too but I do not want to change the key value
Data Array:
const invoices = [
{
number: "BBRFL",
client: "Ellison Daugherty",
amount: 8800,
status: "Paid",
},
{
number: "TYUBK",
client: "Myrna Vinson",
amount: 4097,
status: "Paid",
},
{
number: "IRUBR",
client: "Mcmillan Warner",
amount: 6466,
status: "Draft",
},
];
Here is the app.
const app = new Vue({
el: "#app",
components: {
"create-invoice": CreateInvoice,
"invoice-item": InvoiceItem,
},
data() {
return {
invoices: invoices,
status: null,
};
},
created: function () {
const invoices = localStorage.getItem("invoices");
if (invoices) {
this.invoices = JSON.parse(invoices);
}
},
computed: {
count: function () {
return this.filteredInvoices.length;
},
filteredInvoices: function () {
if (this.status) {
return this.invoices.filter((invoice) => invoice.status == this.status);
}
return this.invoices;
},
},
methods: {
saveInvoice(invoice) {
this.invoices.push(invoice);
},
invoiceUpdate(invoice) {
const index = this.invoices.findIndex((i) => i.number === invoice.number);
// this.invoices.splice(index, 1, invoice);
Vue.set(this.invoices[index], "client", invoice.client);
Vue.set(this.invoices[index], "amount", invoice.amount);
Vue.set(this.invoices[index], "status", invoice.status);
},
invoiceDelete(number) {
const index = this.invoices.findIndex((i) => i.number === number);
this.invoices.splice(index, 1);
},
},
watch: {
invoices: {
deep: true,
handler: function (invoices) {
localStorage.setItem("invoices", JSON.stringify(invoices));
},
},
},
template: `
<div>
<div class="row">
<div class="col-md-4 text-left">
<h1>Invoices</h1>
<p class="text-dark">There are {{count}} Total Invoices</p>
</div>
<div class="col-md-4 text-start" style="margin-top: 10px">
<label for="status">Filter</label>
<select name="filter" id="status" class="form-control" v-model="status">
<option value="Draft">Draft</option>
<option value="Paid">Paid</option>
<option value="Pending">Pending</option>
</select>
</div>
<div class="col-md-4 text-right" style="margin-top: 10px">
<button class="btn btn-primary mt-4" id="createInvoice">
New Invoice
</button>
</div>
</div>
<div class="col-md-12">
<div class="row mt-2 mb-2">
<div
class="invoice col-md-12"
>
<invoice-item
v-for="(n, index) in filteredInvoices"
:key="n.number"
:initial-client="n.client"
:initial-number="n.number"
:initial-amount="n.amount"
:initial-status="n.status"
#update-invoice="invoiceUpdate"
#delete-invoice="invoiceDelete"
></invoice-item>
</div>
<div class="text-center" v-if="filteredInvoices.length === 0"><p>No invoice found</p></div>
</div>
</div>
<create-invoice #saveInvoice="saveInvoice" ></create-invoice>
</div>
</div>
`,
});
I had tried, this.$set, Vue.set, Direct assigning to property, assingment using splice function, but none of working. It only works with the change of value of key in for loop. Which I do not want to update.
Jsfiddle
Any Help? Thanks in advance.
How can i have checked radio button in v-for, if my v-model is empty?
The list of users may be different, so I can't set selectedUserId: 665. I tried to use :checked="index == 0" but it does not work with v-model.
var radioVue = new Vue({
el: "#test",
data: {
selectedUserId: null,
users: [{id: 665, name: 'John'}, {id: 1135, name: 'Edward'}, {id: 7546, name: 'David'}]
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="test">
<div v-for="(user, index) in users">
<input type="radio" :value="user.id" v-model="selectedUserId"> {{ user.name }}
</div>
<span>{{ selectedUserId}}</span>
</div>
You can use watch for that:
watch: {
selectedUserId: {
handler(newVal) {
if (newVal === null || newVal === undefined) {
this.selectedUserId = this.users[0].id
}
},
immediate: true
}
}
for this you can use get/set on computed variable with your v-model
the setter does a standard set while the getter return the wanted users id or if there is not the id of the first user (as in first entry of the array)
var radioVue = new Vue({
el: "#test",
data: {
selectedUserId: 7546,
users: [{id: 665, name: 'John'}, {id: 1135, name: 'Edward'}, {id: 7546, name: 'David'}]
},
computed: {
checkedUserId: {
get() {
// need to use == instead of === because v-model seems to set as string :/
return this.users.some(user => user.id == this.selectedUserId) ? this.users.filter(user => user.id == this.selectedUserId)[0].id : this.users[0].id
},
set(val) {
// doing a parseInt() here would allow you to use === in get
this.selectedUserId = val
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="test">
<div v-for="user in users">
<input type="radio" :value="user.id" name="name" v-model="checkedUserId"> {{ user.name }} - {{ user.id }}
</div>
<span>{{ selectedUserId}}</span>
<input type="text" v-model="selectedUserId">
</div>
I am trying to make a form which contains an input group section, in this group, there are one select box and multiple checkboxes. Checkboxes are populated based on the select box selection. There is also an add and remove button to generate and remove input group. The select box is used with v-model to filtered the checkboxes. But when I generate a new input group and make changes, all checkboxes are changed.
I want them to be isolated. How can I achieve?
Here is my Template.
<template>
<form #submit.prevent="onSubmit">
<div v-for="(variationProduct, index) in variationProducts" :key="variationProduct.id">
<div class="from-group mb-4">
<label class="col-form-label"><b>Categories :</b></label>
<select class="form-control mr-2" ref="categories" v-model="category">
<option value="0">Please select category...</option>
<option v-for="category in categories" :key="category.id" :value="category.id">
{{ category.name }}
</option>
</select>
<div v-if="hasError">
<validation-errors v-if="errors['categories.'+index]" :errors="errors">
{{ errors['categories.'+index][0] }}
</validation-errors>
</div>
</div>
<div class="form-group mb-4">
<label class="col-form-lablel"><b>Variation Products :</b></label>
<div class="row">
<div v-for="filteredVariationProduct in filteredVariationProducts" :key="filteredVariationProduct.id">
<div class="col-12">
<input :id="filteredVariationProduct.id" :value="filteredVariationProduct.id" type="checkbox" ref="variationProducts">
<label :for="filteredVariationProduct.id">{{ filteredVariationProduct.name }}</label>
</div>
</div>
</div>
<div v-if="hasError">
<validation-errors v-if="errors['variationProducts.'+index]" :errors="errors">
{{ errors['variationProducts.'+index][0] }}
</validation-errors>
</div>
</div>
<div class="float-right">
<button #click.prevent="add" class="btn btn-success">Add</button>
<button #click.prevent="remove(index)" class="btn btn-danger">Remove</button>
</div>
<br>
<br>
<hr>
</div>
<input type="submit" class="btn btn-primary" value="Submit">
</form>
</template>
Here is my JS.
<script>
import ValidationErrors from './ValidationErrors.vue';
export default {
components: {
'validation-errors': ValidationErrors,
},
data () {
return {
variationProducts: [],
categories: [
{ id: 1, name: 'Technology'},
{ id: 2, name: 'Business'},
{ id: 3, name: 'Marketing'},
{ id: 4, name: 'Development'},
{ id: 5, name: 'Engineering'},
],
category: 0,
activeVariationProducts: [],
count: 1,
errors: {},
hasError: false,
}
},
methods: {
add: function() {
this.count++;
this.errors = {};
this.hasError = false;
this.variationProducts.push({ id: this.count });
},
remove: function (index) {
if ( this.variationProducts.length > 0 && index > -1) {
this.variationProducts.splice(index, 1);
} else {
alert('Must have at least one input!')
}
},
onSubmit: function() {
console.log(this.$refs.variationProducts.value);
},
generateVariationProducts: function(num) {
for(let i = 1; i <= num; i++) {
let activeVariationProduct = {
id: i,
name: 'Product '+ i,
category_id: i
};
this.activeVariationProducts.push(activeVariationProduct);
}
},
},
computed : {
filteredVariationProducts: function () {
let categoryId = parseInt(this.category);
if (categoryId !== 0) {
let filteredVariationProducts = this.activeVariationProducts.filter((variationProduct) => {
return variationProduct.category_id === categoryId;
});
return filteredVariationProducts;
} else {
let filteredVariationProducts = this.activeVariationProducts;
return filteredVariationProducts;
}
}
},
created () {
this.variationProducts.push({ id: this.count });
this.generateVariationProducts(10);
},
}
</script>
Here is a sample code. This code Shows how you can use multiple Checkboxes that is generated dynamically and how to make them isolated -
new Vue({
el : "#app",
data : {
Items : ["One", "Two", "Three"],
newCheckbox : "",
SelectedItems : {
'One' : "",
'Two' : "",
'Three' : "",
},
},
methods:{
add:function(){
Vue.set(this.SelectedItems, this.newCheckbox, false);
this.Items.push(this.newCheckbox);
},
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.22/vue.min.js" type="text/javascript"></script>
<div id="app">
<div>
<label v-for="(item, index) in Items">
<input type="checkbox" v-bind:key="index" v-model="SelectedItems[item]"> {{ item }}
</label>
</div>
<div>
<input type="text" v-model="newCheckbox">
<button #click="add">Add</button>
</div>
<div>
Output : {{ SelectedItems }}
</div>
</div>
You can dynamically add new checkbox and still they are isolated
I have dynamic list of options which comes from api:
<tr v-for="(option, index) in options">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="toggle" v-model="option.value" #click="toggleOption(option.id, index)">
<label class="custom-control-label" for="toggle">{{ option.value }}</label>
</div>
Method:
toggleOption(id, index) {
let app = this;
let option = this.options[index];
app.loading = true;
option.value = !option.value;
axios.patch('/apiendoint' + id, option)
.then(function (resp) {
app.loading = false;
})
.catch(function (resp) {
});
}
When checkbox is clicked all checkboxes changes, if only one item comes from api everything is working. How to make to work it with multiple checkboxes?
I created basic working example
new Vue({
el: '#app',
data: {
loading: false,
options: [
{id: 1, value: true},
{id: 2, value: true},
{id: 3, value: true},
]
},
methods: {
/*
instead of passing `id` and `index`, just pass `option`
*/
toggleOption(option) {
let app = this;
app.loading = true;
option.value = !option.value;
// REMOVE NEXT LINE to send ajax
return;
axios.patch('/apiendoint/' + option.id, option)
.then(function (resp) {
app.loading = false;
})
.catch(function (resp) {});
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table>
<tr v-for="(option, index) in options">
<td>
<div class="custom-control custom-switch">
<input
:id="'toggle-'+option.id"
type="checkbox"
class="custom-control-input"
v-model="option.value"
#click="toggleOption(option)"
>
<label class="custom-control-label" :for="'toggle-'+option.id">
{{ option.value }}
</label>
</div>
</td>
</tr>
</table>
</div>
Trying to make a search plugin using Vue and I'm having a problem with adding a starting/default value to the list of options. If I comment out the pair or template lines involving the start prop the rest of it works fine, but nothing renders if I leave them in.
Component Code:
Vue.component('search', {
props: {
type: String,
hidein: String,
start: {
type: Object,
default: null
}
},
//props: ['type', 'hidein', 'start'],
data: function () {
return {
search: "",
select: "",
results: [],
};
},
template: '<div #load="loaded"><input :id="hidein" type="text" v-model="search" #keyup="updateList">'+
'<input type="hidden" :name="hidein" v-model="select" class="form-control">'+
'<div v-if="start">Current: <span #click="select=start.id" :class="{\'label label-success\':(select==start.id)}>'+
'+ {{start.name}}</span></div>'+
'<div v-if="results.length">Do you mean:<ul>'+
'<li v-for="result in results" :key="result.id"><span #click="select=result.id" :class="{\'label label-success\':(select==result.id)}">'+
'+ {{result.name}}</span></li>'+
'</ul></div></div>',
methods: {
updateList: function(e) {
var response = [];
console.log("search: "+this.search);
$.ajax({
method: "GET",
url: "/api/search/"+this.type,
data: { key: this.search }
}).done(function( msg ) {
this.results = msg;
console.log(this.results);
}.bind(this));
},
loaded: function () {
this.select=!!this.start ? this.start.id : null;
}
},
});
Component Call:
<search type="ships" hidein="ship_id" ></search>
Can anyone tell me what I'm doing wrong? (Besides the hacked together template, that's hopefully a completely separate issue with the pipeline I'm having)
There is a missing " here
:class="{\'label label-success\':(select==start.id)}
But also, please, use a template literal to make your life easier.
`<div #load="loaded"><input :id="hidein" type="text" v-model="search" #keyup="updateList">
<input type="hidden" :name="hidein" v-model="select" class="form-control">
<div v-if="start">
Current:
<span #click="select=start.id" :class="{'label label-success':(select==start.id)}">
{{start.name}}
</span>
</div>
<div v-if="results.length">
Do you mean:
<ul>
<li v-for="result in results" :key="result.id">
<span #click="select=result.id" :class="{'label label-success':(select==result.id)}">
{{result.name}}
</span>
</li>
</ul>
</div>
</div>`