28

I have a Vue.js app where I have a v-repeat on an array of items. I want to add a newItem to the list of items. When I try this.items.push(this.newItem) the object pushed is still bound to the input. Consider the below:

new Vue({
  el: '#demo',

  data: {
    items: [
      {
        start: '12:15',
        end: '13:15',
        name: 'Whatch Vue.js Laracast',
        description: 'Watched the Laracast series on Vue.js',
        tags: ['learning', 'Vue.js', 'Laracast', 'PHP'],
        note: "Vue.js is really awesome. Thanks Evan You!!!"
      },
      {
        start: '13:15',
        end: '13:30',
        name: "Rubik's Cube",
        description: "Play with my Rubik's Cube",
        tags: ['Logic', 'Puzzle', "Rubik's Cube"],
        note: "Learned a new algorithm."
      }
    ],
    newItem: {start: '', end: '', name: '', description: '', tags: '', note: ''}
  },

  methods: {
    addItem: function(e) {
      e.preventDefault();

      this.items.push(this.newItem);
    }
  }
});

The above will, as expected, push the object that is bound onto the items array. The problem is I want just a copy of the object so it will no longer change when the input changes. See this this fiddle. I know I can do:

addItem: function(e) {
  e.preventDefault();
  this.items.push({
    name:        this.newItem.name,
    start:       this.newItem.start,
    end:         this.newItem.end,
    description: this.newItem.description,
    tags:        this.newItem.tags,
    notes:       this.newItem.notes
  })
}

This works but is a lot of repetition.

The question: Is there a built in way to add just a copy of the object instead of the persistent object.

Elfayer
  • 4,161
  • 8
  • 46
  • 74
DutGRIFF
  • 4,913
  • 1
  • 31
  • 42
  • I understand there could be a columns array in data to generate the columns and it's models etc.. Also the tags field isn't kept as an array. I copied this from a project I am starting and half implemented it for an example. Just ignore these. – DutGRIFF Jun 01 '15 at 17:10

4 Answers4

54

See this issue on GitHub.

Shallow Clone

I was using jQuery's $.extend until Evan You pointed out there is an undocumented built it extend function Vue.util.extend that does a shallow clone. So what you could use is:

addItem: function(e) {
  e.preventDefault();

  this.items.push(Vue.util.extend({}, this.newItem));
}

See the updated Fiddle.

Deep Clone

When doing a shallow clone on an object that references other objects you copy the references to the external objects instead of cloning them. To clone the object completely do a Deep Clone.

For the deep clone, per Evan's suggestion in the first link, one could use: JSON.parse(JSON.stringify(object)). This can be seen between this fiddle and this fiddle.

If using lodash check out lodash cloneDeep. If using NPM check out clone-deep.

DutGRIFF
  • 4,913
  • 1
  • 31
  • 42
  • 9
    Evan's comment on that issue states that it is a shallow clone. Only the top level properties are cloned. – patricus Mar 22 '17 at 16:05
14

This didn't work for me (vue 1.0.13). I used the following to create a copy without the data bindings:

this.items.push( JSON.parse( JSON.stringify( newItem ) ) );
user3698965
  • 141
  • 1
  • 4
8

You can use Vanilla JavaScript with Object.assign():

addItem: function(e) {
  e.preventDefault();

  this.items.push(Object.assign({}, this.newItem));
}

UPDATE:

You can also use Object Spread:

addItem: function(e) {
  e.preventDefault();

  this.items.push({...this.newItem});
}
Arthur Ronconi
  • 2,032
  • 23
  • 21
4

The top answer is wrong. Vue.util.extend has nothing to do with jQuery's extend. It's always a shallow clone. https://github.com/vuejs/vue/issues/1849#issuecomment-158770874

Object.assign and Spread operator are also shallow copy. see this https://scotch.io/bar-talk/copying-objects-in-javascript

Simply use the implementation of Ramda.js https://github.com/ramda/ramda/blob/v0.26.1/source/internal/_clone.js

_curry is not necessary if you don't want them.

Or check this MVA What is the most efficient way to deep clone an object in JavaScript?

Alvin Smith
  • 439
  • 4
  • 8