6

Simply, I just want to change the entire data of a Vue object, like this:

vueobj.$data = newdata; 

but the official document says it's not allowed:

VM156 vue.js:597 [Vue warn]: Avoid replacing instance root $data. Use nested data properties instead.

(found in )

So I tried another way: first destroy the the vueobj by $destroy(), then create a new vueobj to bind new data object to the same the UI element, but after that, the UI element still uses the old data. So how could I solve this problem? thanks!

acdcjunior
  • 132,397
  • 37
  • 331
  • 304
Jinmin
  • 205
  • 1
  • 3
  • 10

3 Answers3

11

this works for me:

<div id="app">
  {{name}}: {{age}}
</div>

const app = new Vue({
  el: "#app",
  data: {
    name: "Alice",
    age: 30
  },
  methods: {
    refresh(data) {
      Object.assign(this.$data, data); // <--
    }
  }
});

const newData = {
  name: "Bob",
  age: 22
};

app.refresh(newData);
tomloprod
  • 7,472
  • 6
  • 48
  • 66
tinos
  • 562
  • 6
  • 12
  • This will break reactivity... so anything you need to be reactive has to be in computed or $set. – Ray Foss May 07 '21 at 00:26
  • 1
    Why would it break reactivity @RayFoss ? `Object.assign` calls setters on the target object (in this case `this.$data`), which is all that vue2 reactivity system uses. In other words, using `Object.assign(this.$data, source)` is the same as if you assigned to each individual property in `this.$data` manually. – feralgeometry Jun 01 '21 at 14:09
  • Can confirm this does not break reactivity. It is the best answer. – Rob Feb 16 '22 at 04:42
7

It is tricky, but you could iterate over the data properties and:

  • First: remove all data properties;
  • Second: add each property of the newdata object to the data object.

Demo below:

new Vue({
  el: '#app',
  data: {
    name: 'Alice',
    age: 30
  },
  methods: {
    changeWholeData() {
      let newdata = {name: 'Bob', age: 40};
       // erase all current keys from data
       Object.keys(this.$data).forEach(key => this.$data[key] = null);
       // set all properties from newdata into data
       Object.entries(newdata).forEach(entry => Vue.set(this.$data, entry[0], entry[1]));
    }
  }
})
<script src="https://unpkg.com/vue"></script>

<div id="app">
  <p>Name: {{ name }}</p>
  <p>Age: {{ age }}</p>
  <button @click="changeWholeData">Change Whole Data</button>
</div>
acdcjunior
  • 132,397
  • 37
  • 331
  • 304
  • Sure! Btw, may I ask why you chose the other? (Just curiosity, really, the other is a great solution as well, you should keep it as accepted!) – acdcjunior Mar 30 '18 at 01:09
  • I'm a newbie of using Vue, and I was a coder using Microsoft ASP.NET, in ASP.NET, it's easy to set element.DataSource=somedata, so I thought Vue should be in the same way, now I know I was wrong. Thank you, you are so kind. – Jinmin Apr 03 '18 at 12:58
  • @Jinmin You were only wrong if you want to be wrong. You can easily add a `dataSource` property to your data object to wrap the data you want to swap out. Use this to create your Vue root: `var data = { dataSource: originalData };` then you can replace it like this `vueobj.dataSource = newdata;` – xr280xr Nov 22 '19 at 00:18
2

As it says, you need to use a nested property to wrap the data you want to replace so it just takes a little planning up front.

If you create your Vue instance like this,

var vueobj = new Vue({
  el: '#app',
  data: {
    dataSource: {
        name: 'Alice',
        age: 30
    }
  }
})

then you can replace your data like this

vueobj.dataSource = newdata;

The dataSource wrapper property can be any valid property name. The downside is you can no longer access your inner properties directly on the Vue instance. E.G. you have to use vueobj.dataSource.name instead of vueobj.name, but I guess that's the trade off for all the other ease of use Vue is providing. You could create a name computed to solve this on a case-by-case basis but it would be tedious to do if there are many root properties.

xr280xr
  • 12,621
  • 7
  • 81
  • 125