Search code examples
javascriptvue.jsreactive-programming

How to declare a nested reactive component?


When reading "Reactivity in Depth" in the documentation, I see two points which I am not sure how to interpret:

When you pass a plain JavaScript object to a Vue instance as its data option, Vue will walk through all of its properties and convert them to getter/setters using Object.defineProperty

(...)

Due to the limitations of modern JavaScript (and the abandonment of Object.observe), Vue cannot detect property addition or deletion. Since Vue performs the getter/setter conversion process during instance initialization, a property must be present in the data object in order for Vue to convert it and make it reactive.

How does this related to nested object such as

[
  {
    "a": 1,
    "b": [
      {
        "c": 1
      },
      ...
    ]
  },
  {
    "a": 10,
    "b": [
      {
        "c": 10
      },
      ...
    ]
  },
  ...
]

Specifically, how should such object be presented to data() so that it is reactive?

data() {
    return {
        likeThis: [],
        orLikeThat: [{}],
        orMoreDetailed: [{a: null, b:[]}],
        orTheFullStucture: [{a: null, b:[{c: null}]}]
    }
}

Solution

  • I think that you are misunderstanding the property addition/deletion caveat. The statement in the first section of the docs that you reference holds true for your example object. If you set that object as a property of the object returned by the data method, everything within that object will be reactive.

    Here's a simple example where I've set your object as myNestedData in the data method. I've bound myNestedData[0].b[0].c via v-model to an input element and you can see that changes to that value are reflected in the myNestedData object itself.

    Vue.config.productionTip = false;
    Vue.config.devtools = false;
    
    new Vue({
      el: '#app',
      data() {
        return {
          myNestedData: [{
            "a": 1,
            "b": [{ 
              "c": 1
            }]
          }, {
            "a": 10,
            "b": [{
              "c": 10
            }]
          }]
        };
      }
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
    <div id="app">
      <input v-model="myNestedData[0].b[0].c" type="number">
      <br>
      {{ myNestedData }}
    </div>


    Any of the examples you gave in your final section of code would be fully reactive, as in my example above. The property addition/deletion caveat only comes into play if you attempt to add or remove properties from those reactive data objects.

    Here's a simple example where a foo property is initially set in the data method with a value of [{ a: 'apple' }]. Then this.foo[0].b is set to 'banana' in the created hook of the component. But, because the b property of this.foo[0] didn't exist before, that property is not reactive. You can see this by changing the value of the second input, which is bound to via v-model to foo[0].b. Changing that value does not update the value that property in foo.

    Vue.config.productionTip = false;
    Vue.config.devtools = false;
    
    new Vue({
      el: '#app',
      data() {
        return {
          foo: [{ a: 'apple' }]
        };
      },
      created() {
        this.foo[0].b = 'banana';
      }
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
    <div id="app">
      <input v-model="foo[0].a">
      <input v-model="foo[0].b">
      <br>
      {{ foo }}
    </div>