0

I'm using vue.js extends for the first time. I have a component that extends another and it needs to read the root components data to update the status in its own component.

What I'm finding is that the component that extends the other only seems to take a copy of the root's data when it's rendered but if I update a property in the root component it's not updated in the extended component.

So I might not be going about this the right way if the extended component doesn't update when the root does. For example I want to check the length of an array on the root component and update another data value. It updates the value on the root but not on the extended component.

Is this the expected behaviour and is there a way I can send the updated data down to the extended component?

Sample code:

<a inline-component>
<input type="text" v-model="myArray" />
<button v-on:click="saveData">Save</button>
</a>

<b inline-component>
<div v-if="myArray.length > 0">On Target</div>
</b>

var a = Vue.component('a', {
   data: function () {
       return {
           myArray: [],
       }
   },

   methods: {

       saveData : function(){
           var vm = this;

           axios.post('/save', {
             })
             .then(function (response) {
               vm.myArray = response.data;
            })
            .catch(function (error) {
               console.log(error);
            }); 
        },

    }
});

Vue.component('b', {
    extends: a,
});

1 Answer 1

1

I have a component that extends another and it needs to read the root components data to update the status in its own component.

For the purposes of my answer I'm going to assume that you have two component definitions, A and B, and B extends A. I assume that when you say root you just mean A.

What I'm finding is that the component that extends the other only seems to take a copy of the root's data when it's rendered but if I update a property in the root component it's not updated in the extended component.

Rendering is not really relevant here. The data properties are set up when a component instance is created. Typically rendering will happen just after creation but merging any data happens much earlier in the component life-cycle. Even if the component isn't rendered the data will still be initialised.

No copying takes place. Let's consider a data function on component A:

data () {
   return {
      myArray: []
   }
}

Every time this function is invoked it is going to return a new object, each containing a new array. This is precisely what happens if you create an instance of A directly. For each instance, Vue will call this function and get a new object defining the data. Generally that's what you'd want, rather that having components sharing data.

Now let's consider B. That might define its own data function. When an instance of B is created Vue will call the data function for both A and B and then merge the objects. No copying takes place, just merging. If you want to know more about how Vue handles merging in general see the documentation but for data the strategy is pretty simple. Properties from both objects will be combined with B taking precedence over A if there's a clash of property names. There is no recursive merging of properties.

So the idea of updating 'a property in the root component' is not particularly well-defined. You might be thinking of it as a bit like a prototype chain, where modifying a superclass would impact the subclass, but that isn't what's going on here. The data functions are invoked when the component is created and that's that. There isn't a lasting link back to the component definition like there is with a prototype chain.

If you really want all your component instances to share the same data value then it can be done, you just need to make sure that the data function is returning the same object/array every time. e.g.

const myArray = []

export default {
  name: 'A',

  data () {
    return {
      myArray
    }
  }
}

Written this way all instances of A will share the same array for myArray. So long as B doesn't define it's own value for myArray it will share it too.

For example I want to check the length of an array on the root component and update another data value. It updates the value on the root but not on the extended component.

I'm struggling a bit to understand what that means. It seems there are lots of assumptions about things being shared, single instances here. It's not entirely clear how you update the 'root' given it's a component definition and not a component instance.

If possible you should use a computed property for this. That would be inherited by B. Each instance of A (or B) would have their own value for this computed property, which might be a little wasteful if they're all going to be the same, but it's probably still the best way to go.

You could in theory use a watch. That should be inherited too but keep in mind it would be manipulating values for that particular instance.

Reading between the lines a little, if you wanted to update something on the 'root' so that it magically appeared in the subcomponents you could use the same shared reference-type trickery that I demonstrated earlier for myArray. You may need to be careful with how you update it though. If, for example, you used a watch you might find the you end up updating the same object many times, once for each instance of the component.

Update:

Based on the code you've posted it could be made to work something like this:

var myArray = [];

var a = Vue.component('a', {
   data: function () {
       return {
           myArray: myArray // Note: using the same, shared array
       }
   },

   methods: {

       saveData : function(){
           var vm = this;

           axios.post('/save', {
             })
             .then(function (response) {
               // Note: Updating the array, not replacing it
               var myArray = vm.myArray;
               myArray.splice(0, myArray.length);
               myArray.push.apply(myArray, response.data);
            })
            .catch(function (error) {
               console.log(error);
            }); 
        },

    }
});

Vue.component('b', {
    extends: a,
});

Your example didn't include any ES6 so I've refrained from using it but it would be a bit simpler if that were available.

The example above works by sharing the same array between all instances of the component and then mutating that instance. Assigning a new array to that property won't work as it would only update that particular component instance.

However, all that said, this is increasingly looking like a case where you should give up on trickery and just use the Vuex store instead.

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

4 Comments

Thank you for the very detailed response! So on the last part, I have <b> and this needs to check the length of an array which is contained in the data of <a>. When I save something in <a> and update the value of the array in the data of <a> it's not updated in <b>, <b> is an empty array and doesn't reflect the update to the array in <a>. I hope that makes sense.
@BarryWalsh I think you need to update the question with some example code to demonstrate what you're describing. You're blurring the lines between component definitions and component instances such that it's very difficult to get a handle on what you really mean. It increasingly sounds like you just want a single copy of the data, which would make the Vuex store a better choice than inheritance shenanigans.
@skirte I've updated my question and added a stripped back version of my code. I don't think it's possible but if I could have an exact copy of the A component on the page twice that would work too.
@BarryWalsh I've updated my answer accordingly. I don't really understand your comment about having an exact copy of the component A on the page twice. If you just want two instances of A then just put two on the page at once. You seem to be expecting them to magically share the same data but that isn't going to happen. Extending A to B isn't going to change that, the instances will still be separate components with separate data.

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.