Mutiple checkboxes, bound to the same Array in Vue JS - javascript

I'm starting out with vuejs and a vue grid at https://jsfiddle.net/kc11/7fqgavvq/
I want to display the checked row objects in the:
<pre> {{ selected| json}} </pre>
area of code,
as in the documentation at http://vuejs.org/guide/forms.html#Checkbox in the "Mutiple checkboxes, bound to the same Array:" example
As you can see when I check 1 checkbox, all are selected. Why is this happening? How can I fix this?

You made a few wrong assumptions in your code (mainly in respect to the scope).
You have your selected array in your main instance, instead of the demo-grid component, where you have your checkboxes:
var demo = new Vue({
el: '#demo',
data: {
searchQuery: '',
gridColumns: ['name', 'power'],
gridData: [
{name: 'Chuck Norris', power: Infinity},
{name: 'Bruce Lee', power: 9000},
{name: 'Jackie Chan', power: 7000},
{name: 'Jet Li', power: 8000}
],
selected: [] // <- This one
}
})
And there is no selectAll method defined on your demo-grid component either, even though you reference it in your template:
<input #click="selectAll" type="checkbox" v-model="selected" id="{{ entry.name }}" value="{{ entry.name }}"></td>
If you thus pass your selected property into your demo-grid, (and define it in the props), you should be fine:
<demo-grid
v-bind:data="gridData"
v-bind:columns="gridColumns"
v-bind:filter-key="searchQuery"
v-bind:selected="selected"> <!-- here -->
</demo-grid>
And define a selectAll method:
methods: {
...
selectAll: function () {
...
}
Here you can see a working example:
https://jsfiddle.net/7fqgavvq/3/

You should add the selected key to the component's data, not to the main instance of vue.
https://jsfiddle.net/7fqgavvq/4/

Related

vue.js computed property not triggered

Vue JS computed property is not triggered With this markup
<!-- language: lang-html -->
<p>£{{plant_price}}</p>
<div v-if="selected.plant.variations.length > 0 ">
<select v-model="selected.plant.selected_variation" class="form-control">
<!-- inline object literal -->
<option v-for="(variation, i) in selected.plant.variations" :selected="variation.id == selected.plant.selected_variation ? 'selected' : ''":value="variation.id">
{{variation.name}}
</option>
</select>
</div>
<!-- language: lang-js -->
var app = new Vue({
el: '#vueApp',
data: {
selected: {
type: {a: '' , b: ''},
vehicle: '',
plant: {
}
},
computed: {
plant_price: function() {
if (this.selected.plant.variations.length > 0 ) {
var variant = _.find(this.selected.plant.variations, {id: this.selected.plant.selected_variation });
return variant.price;
} else {
return this.selected.plant.price;
}
}
...
selected.plant is populated by clicking on a plant - triggering the updateSelected method.
<div class="col-sm-4" v-for="(plant, i) in step2.plants">
<div v-on:click="updateSelected(plant)" ....
methods: {
updateSelected: function(plant) {
this.selected.plant = plant; // selected plant
if (this.selected.plant.variations.length > 0 ) {
this.selected.plant.selected_variation = this.selected.plant.variations[0].id; // set the selected ID to the 1st variation
I have checked through the debugger, and can see that all the correct properties are available.
selected:Object
type:Object
vehicle: "Truck"
plant:Object
id:26
price:"52"
regular_price:"100"
selected_variation:421
variations:Array[2]
0:Object
id:420
name:"small"
price:52000
regular_price:52000
1:Object
etc...
I have a computed property, which should update the plant_price based on the value of selected.plant.selected_variation.
I grab selected.plant.selected_variation and search through the variations to retrieve the price. If no variation exists, then the plant price is given.
I have a method on each product to update the selected plant. Clicking the product populates the selected.plant and triggers the computed plant_price to update the price (as the value of selected.plant.selected_variation has changed).
However, the computed plant_price is not triggered by the select. Selecting a new variant does what its supposed to, it updates selected.plant.selected_variation. Yet my plant_price doesn't seem to be triggered by it.
So I refactored my code by un-nesting selected.plant.selected_variation. I now hang it off the data object as
data = {
selected_variation: ''
}
and alter my computer property to reference the data as this.selected_variation. My computed property now works??? This makes no sense to me?
selected.plant.selected_variation isn't reactive and VM doesn't see any changes you make to it, because you set it after the VM is already created.
You can make it reactive with Vue.set()
When your AJAX is finished, call
Vue.set(selected, 'plant', {Plant Object})
There're two ways you can do it, what you are dealing with is a nested object, so if you want to notify the changes of selected to the others you have to use
this.$set(this.selected, 'plant', 'AJAX_RESULT')
In the snippet I used a setTimeout in the created method to simulate the Ajax call.
Another way you can do it is instead of making plant_price as a computed property, you can watch the changes of the nested properties
of selected in the watcher, and then update plant_price in the handler, you can check out plant_price_from_watch in the snippet.
Vue.component('v-select', VueSelect.VueSelect);
const app = new Vue({
el: '#app',
data: {
plant_price_from_watch: 'not available',
selected: {
type: {a: '' , b: ''},
vehicle: "Truck"
}
},
computed: {
plant_price() {
return this.setPlantPrice();
}
},
watch: {
selected: {
handler() {
console.log('changed');
this.plant_price_from_watch = this.setPlantPrice();
},
deep: true
}
},
created() {
setTimeout(() => {
this.$set(this.selected, 'plant', {
id: 26,
price: '52',
regular_price: '100',
selected_variation: 421,
variations: [
{
id: 420,
name: "small",
price: 52000,
regular_price: 52000
},
{
id: 421,
name: "smallvvsvsfv",
price: 22000,
regular_price: 22000
}
]
})
}, 3000);
},
methods: {
setPlantPrice() {
if (!this.selected.plant) {
return 'not available'
}
if (this.selected.plant.variations.length > 0 ) {
const variant = _.find(this.selected.plant.variations, {id: this.selected.plant.selected_variation });
return variant.price;
} else {
return this.selected.plant.price;
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="app">
<p>£{{plant_price}}</p>
<p>£{{plant_price_from_watch}}</p>
<div v-if="selected.plant && selected.plant.variations.length > 0 ">
<select v-model="selected.plant.selected_variation" class="form-control">
<!-- inline object literal -->
<option v-for="(variation, i) in selected.plant.variations" :selected="variation.id == selected.plant.selected_variation ? 'selected' : ''":value="variation.id">
{{variation.name}}
</option>
</select>
</div>
</div>

Reading checkbox values from vue.js component

A few days ago I started using vue.js and trying to get the hang of it.
I've been fiddling quite a bit to get this easy example to work: reading the value of selected checkboxes in components with vue.js .
Please see my example on http://jsbin.com/gukoqo/edit?html,js,output
How can I let selected in the parent instance contain the selected values of the checkbox? E.g., filter_a and filter_c are selected, then selected should contain an array: ['filter_a', 'filter_c']
I expected vue.js to make this very easy, but don't know yet how to. Anyone? :)
I'm using the latest vue.js (2.3.3 at the moment)
One possible way.
Vue.component('facet-filter', {
props: ['filter', 'checked'],
template: `<div>
<label class="form-check-label">
<input #change="$emit('change', filter.text, $event)"
class="form-check-input"
type="checkbox"
:value="filter.text"
:checked="checked"
name="filters"> {{filter.text}}
{{$props | json 2}}</label>
</div>`,
});
new Vue({
el: '#app',
data: {
filterFacets: [
{ id: 0, text: 'filter_a' },
{ id: 1, text: 'filter_b' },
{ id: 2, text: 'filter_c' },
{ id: 3, text: 'filter_d' },
],
selected: [], // How can I let this contain ['filter_a', 'filter_b'] etc. when selected?
},
methods:{
onChange(filter, $event){
if ($event.target.checked)
this.selected.push(filter)
else {
const index = this.selected.findIndex(f => f === filter)
if (index >= 0)
this.selected.splice(index, 1)
}
}
}
});
And change your template to
<div id="app">
<facet-filter
v-for="item in filterFacets"
v-bind:filter="item"
v-bind:checked="selected.includes(item.text)"
:key="item.id"
#change="onChange"
>
</facet-filter>
<p><pre>data: {{$data | json 2}}</pre></p>
</div>
Updated bin.

VueJS 2.0 v-model dynamic target inside v-for

I've got a form with about 10 select elements built from an array in my Vue data.
The array of selectors is empty initially and then an AJAX call populates the array and Vue builds the HTML - I've kept the snippet below simplified just to demonstrate the issue I'm having with v-model
I want to create an object that has all the selected values in it, so I'm trying to use v-model="selected[ selector.name ]" as per the example below.
I want to easily be able to ask for selected.make or selected.fuel
Now this works if I initialize the selected property like this:
selected: { make: 'audi', fuel: 'petrol' }
If I leave it blank, like in the example, {}, then it doesn't get updated.
I don't want to manually hardcode all the properties of selected, I only want to be listing them once in the server side code that gets sent via AJAX
So am I missing something completely obvious, should I be doing this in a different way?
Maybe a method to find the dropdown that matches a field name and returns the value? Just that doesn't seem like a very Vue thing to do.
var app = new Vue({
el: '#example',
data: {
selectors: [
{
name: 'make',
options: ['audi','bmw']
},
{
name: 'fuel',
options: ['petrol','diesel']
}
],
selected: {}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.js"></script>
<div id="example">
<template v-for="selector in selectors">
<select v-model="selected[ selector.name ]">
<option v-for="option in selector.options">{{option}}</option>
</select>
</template>
<p>
{{selected.make}}
<br />
{{selected.fuel}}
</p>
</div>
it's probably becuase you're not setting new keys on an object with this.$set
try:
this.$set(this.selected, 'make', 'audi')
Not using this.$set - alias of Vue.set - will mean Vue doesn't set the new key as reactive, and in turn won't be watching for any updates to it, docs: https://v2.vuejs.org/v2/api/#vm-set
var app = new Vue({
el: '#example',
data: {
selectors: [{
name: 'make',
options: ['audi', 'bmw']
}, {
name: 'fuel',
options: ['petrol', 'diesel']
}],
selected: null,
},
created () {
// this would happen following your ajax request - but as an example this should suffice
this.selected = {}
this.selectors
.forEach((selector) => {
this.$set(this.selected, selector.name, '')
})
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.js"></script>
<div id="example">
<div v-if="selected">
<select v-model="selected[selector.name]" v-for="selector in selectors">
<option :value="option" v-for="option in selector.options">
{{option}}
</option>
</select>
<p>make: {{selected.make}}<p>
<p>fuel: {{selected.fuel}}</p>
<pre>{{ selected }}</pre>
</div>
</div>

Component Vuejs2 declare $data obj to share data between components

I am trying to make a Vue2 component to all the select of my app so would be easier later to change it if necessary!
I've based my research on the example given by the docs and I am breaking my head to figure out why should I speficy all the object on the data attr to make it work!
The following code is working properly, but if we change:
data: { record: { category_id: null } } by data: { record: {} } it stop to work!
Must be said the $data.record is loaded by ajax... would I always specify the whole object even knowing that after the ajax request I am going to replace all with something like this.record = response.data?
If somebody need there is FIDDLE [ https://jsfiddle.net/gustavobissolli/4xrfy54e/1/ ]
EDIT: SORRY GUYS JUST FIXED FIDDLE LINK
Vue.component('select2', {
props: ['options', 'value'],
template: '#select2-template',
data() {
return {
model: ''
}
},
mounted: function() {
this.model = this.value
},
watch: {
value: function(value) {
this.model = value
},
model: function(value) {
this.$emit('input', value)
},
}
})
var vm = new Vue({
el: '#el',
template: '#demo-template',
data: {
record: {
category_id: null
},
options: [{
id: 1,
text: 'Hello'
}, {
id: 2,
text: 'World'
}]
}
})
<div id="el"></div>
<!-- using string template here to work around HTML <option> placement restriction -->
<script type="text/x-template" id="demo-template">
<div>
<pre>{{ $data | json }}</pre>
<select2 :options="options" v-model="record.category_id" value="record.category_id"></select2>
</div>
</script>
<script type="text/x-template" id="select2-template">
<select v-model="model">
<option disabled>Select...</option>
<option v-for="opt in options" :value="opt.id">{{ opt.text }}</option>
</select>
</script>
So you are trying to edit a value which didn't arrive yet? :-)
The thing is: at the moment v-model="record.category_id" is "executed", you have nothing there, ie, there is no "category_id" at the "record" object. So, it binds to nothing. This is why the select won't work if you omit the "category_id" at data initialization.
But your assumption that when data arrives from server (ajax call) the component will not work, is wrong.
I have updated your fiddle: https://jsfiddle.net/4xrfy54e/4/
First, use the dropdown before clicking the button: since it is binded to nothing, it will not update anything. This is correct.
Now, click the button. The button is simulating that data arrived from the server, and is assigned to this.record of the vm.
Play with the dropdown again: since record.category_id exists now, the binding is working fine.
Please, read the "Reactivity in Depth" documentation page, and you will stop breaking your head :-)

Data binding in nested custom polymer elements (recursive data-binding)

I try to bind custom sub elements to values of local storage by using polymer's template repeat functionality like this:
<polymer-element name="aw-outerElement">
<template>
<template repeat="{{group in grouplist}}">
<aw-innerElement groupId="{{group.groupId}}" name="{{group.name}}" val="{{group.val}}"></aw-innerElement>
</template>
</template>
<script>
Polymer('aw-outerElement', {
ready : function () {
// Binding the project to the data-fields
this.prj = au.app.prj;
this.grouplist = [
{ groupId: 100, name: 'GroupName1', val: this.prj.ke.groupVal100},
{ groupId: 200, name: 'GroupName2', val: this.prj.ke.groupVal200}
];
}
</script>
In the code above I try to pass the data binding this.prj.ke.groupVal100 and this.prj.ke.groupVal200
to my inner element aw-innerElement through the attribute val. The aw-innerElement is a custom paper-input element where the value attribute should be set to e.g. this.prj.ke.groupVal100. It seems that only the stored initial value 0 will be set and NOT the data-binding string this.prj.ke.groupVal100 inside the value attribute. Is there a way to make a data-binding with template repeat inside inner elements?
My inner elements looks like this:
<polymer-element name="aw-innerElement" attributes="groupId name val">
<template>
<paper-input type="number" floatingLabel label="{{groupId}} {{name}}" value="{{val}}" error="{{i18nnrerror}}"></paper-input>
</template>
<script>
Polymer('aw-innerElement', {
publish: {
groupId: 0,
name: '',
val: 0
},
ready : function () {
// Binding the project to the data-fields
this.prj = au.app.prj;
...
}
</script>
As you can see above the value="{{val}}" of my innerElement should be set to this.prj.ke.groupVal100 and this.prj.ke.groupVal200.
Thanks in advance!
I know I'm digging up an old question, but for future searchers this might come in handy.
Polymer does not allow a variable as your key, so you need to pull it through a function like so:
...
<template is="dom-repeat" items="{{users}}">
<li>{{showValue(item)}}</li>
</template>
...
<script>
Polymer('aw-outerElement', {
// Standard Polymer code here
showValue: function(item){
return item[myVar];
}
});
</script>
You can then manipulate as much as you want in Javascript and return the output for that one item in items.
Passing in a value seems to work fine for me in this example: http://jsbin.com/kalih/4/edit
<polymer-element name="x-bar">
<template>
<paper-input id="input"
label="{{name}}"
value="{{val}}">
</paper-input>
<button on-tap="{{logVal}}">Log val</button>
</template>
<script>
Polymer('x-bar', {
publish: {
val: 0
},
logVal: function() {
console.log(this.$.input.value);
}
});
</script>
</polymer-element>
<polymer-element name="x-foo">
<template>
<template repeat="{{item in items}}">
<x-bar name="{{item.name}}" val="{{item.val}}"></x-bar>
</template>
</template>
<script>
Polymer('x-foo', {
ready: function() {
this.items = [
{
name: 'baz',
val: 28
},
{
name: 'qux',
val: 42
}
];
}
});
</script>
</polymer-element>
<x-foo></x-foo>
Btw, your aw-innerElement doesn't need to have an attributes attribute because you're using the publish object (they serve the same purpose, but publish lets you set default values, which you've done). Also, we recommend that you don't camel-case your element names, the HTML parser is actually going to lowercase them all anyway.
I'm new with polymer so maybe my answer is not correct. But I done something like this that you're trying to do.
here is a sample of my code
I guess that your problem is that you didn't use bind on your template
<template bind="{{grouplist}}">
<template repeat="{{group in grouplist}}">
</template>
</template>
<script>
Polymer('aw-outerElement', {
ready : function () {
// Binding the project to the data-fields
this.prj = au.app.prj;
this.grouplist = [
{ groupId: 100, name: 'GroupName1', val: this.prj.ke.groupVal100},
{ groupId: 200, name: 'GroupName2', val: this.prj.ke.groupVal200}
];
}
</script>

Categories