2

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?

4 Answers 4

2

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 :)

Sign up to request clarification or add additional context in comments.

Comments

2

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!

Comments

2

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>

Comments

0

Not really a vue thing - just plain JS:

arr.map(obj => { return {...obj, newProperty: "sameValueAsOthers"}});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.