Reputation: 7879
In Vue.js, in order to add a property/array item to something already in the virtual DOM, you have to use the $set function.
Here are the wrong ways:
Object: this.myObject.newProperty = "value";
Array: this.myArray[3] = object;
Here's the right way:
Object: this.$set(this.myObject, "newProperty", "value");
Array: this.$set(this.myArray, 3, object);
My question is how do you $set a property of all objects in an array?
Here's the wrong way:
for (var i = 0; i < this.myArray.length; i++) {
this.myArray[i].newProperty = "value";
}
So, what's the method for me to use $set to do this?
Upvotes: 2
Views: 4631
Reputation: 805
You simply just wanna add a new property to objects in an array. Not set a new value to the array by their index. You can do the following:
new Vue({
el: '#demo',
data: {
myArray: [
{id: 1},
{id: 2},
{id: 3},
{id: 4},
{id: 5}
]
},
methods: {
addProperties() {
for (var i = 0; i < this.myArray.length; i++) {
this.$set(this.myArray[i], 'newProperty', 5 - i)
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<div v-for="item in myArray" :key="item.id">
<span>{{item.id}}: </span>
<span v-if="item.newProperty">{{item.newProperty}}</span>
</div>
<button @click="addProperties">Add Properties</button>
</div>
Upvotes: 2
Reputation: 8329
A bit tweaked code of yours works:
new Vue({
el: "#app",
data: {
todos: [{
text: "Learn JavaScript",
done: false
},
{
text: "Learn Vue",
done: false
},
{
text: "Play around in JSFiddle",
done: true
},
{
text: "Build something awesome",
done: true
}
]
},
methods: {
toggle: function(todo) {
todo.done = !todo.done
},
changeProperty1() {
const val = 'A'
// this is your code a bit modified
// defining length (and using it in the comparison) is a
// bit of optimization, not required
for (var i = 0, length = this.todos.length; i < length; i++) {
this.$set(this.todos[i], 'property1', val);
}
},
changeProperty1Again() {
for (todo of this.todos) {
if (todo.property1) {
todo.property1 = 'B'
}
}
}
},
created() {
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="todo in todos">
<label>
<input type="checkbox"
v-on:change="toggle(todo)"
v-bind:checked="todo.done">
<del v-if="todo.done">
{{ todo.text }}
</del>
<span v-else>
{{ todo.text }}
</span>
<span>
{{ todo.property1 }}
</span>
</label>
</li>
</ol>
<button @click="changeProperty1">Click this first</button>
<button @click="changeProperty1Again">Click this second</button>
</div>
Sorry for the lengthy snippet, I just copied it over from JSFiddle :)
Upvotes: 2
Reputation: 7668
You keep doing this.$set(this.myArray, 3, object);
in a loop using the index.
Something like this after modifying your object.
var newObject = Object.assign({}, this.myArray[i], {newProperty: 'value'} ); // Immutable object created
this.$set(this.myArray, i, newObject);
This will be inefficient as it will call $set
for each iteration.
so you can do a map on your array and return a new Object from inside.
const newArray = myArray.map(object => {
return Object.assign({}, object, {newProperty: 'value'} );
//or by ES6 spread operator
return {...object, newProperty: 'value'};
});
Then set your array for Vuejs to re-render.
Hope, this will give the idea. Although context(this) may vary depending on how you're implementing!
Upvotes: 2
Reputation: 824
Not really a vue thing - just plain JS:
arr.map(obj => { return {...obj, newProperty: "sameValueAsOthers"}});
Upvotes: 0