35

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.

1
  • 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. Commented Jun 1, 2015 at 17:10

6 Answers 6

57

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.

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

4 Comments

Evan's comment on that issue states that it is a shallow clone. Only the top level properties are cloned.
i tried clone-deep and it's working fine. I just wanted to make sure I have a sort of snapshot copy of an obj that had a vue component in there and _.cloneDeep and most libs and functions don't work well, but clone-deep did it for me.
I fixed clone-deep so it handles circular references, see github.com/emahuni/clone-deep.git
This article was a good read: code.tutsplus.com/articles/…
16

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

1 Comment

I meant DutGRIFF's answer: this.items.push(Vue.util.extend({}, this.newItem));
12

UPDATE 2024:

You should use structuredClone to clone removing the objects references:

  addItem: function(e) {
    e.preventDefault();
  
    this.items.push(structuredClone(this.newItem));
  }

UPDATE:

You can also use Object Spread:

  addItem: function(e) {
    e.preventDefault();
  
    this.items.push({...this.newItem});
  }

OLD ANSWER:

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

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

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

2 Comments

Object.assign creates a shallow copy. I am assuming that the object spread also creates a shallow copy, right?
Not a deep. Make this simple test: a = { nivel1: { nivel2: { nivel3: 'nivel 3' } }, } b = a b.nivel1.nivel2.nivel3 = 'new value' a.nivel1.nivel2.nivel3 // 'new value'
6

structuredClone can be used but there is a little gotcha. It cannot clone an Proxy which is the way Vue 3 implements reactivity. So you must first get the target and clone this:

import { toRaw } from 'vue'
...
const someCopy = structuredClone(toRaw(someObject))

Comments

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?

Comments

0

Using lodash

import _ from 'lodash';


this.form_data = _.cloneDeep(this.form_data_initial);

this.form_data = _.merge(_.cloneDeep(this.form_data), { filter: _.cloneDeep(this.form_data_initial.filter) });

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.