Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Props passed to dynamic component in Vue are not reactive

I am using Vue's dynamic component to load a component depending on what is to be displayed. As these components all have different props I build an object and pass it in via v-bind depending on what I need to use from the original state model.

However, when I do this I lose the reactive nature of Vue's props data flow. The code sample below shows an example of this with the name changing on the standard component but not on the dynamic version.

I expect this is something to do with the string value being copied into the new object, rather than a reference to the original reactive property. Can anyone advise on how I can make this work as expected?

Vue.config.productionTip = false;

Vue.component("example-component", {
    props: ["name"],
    template: '<span style="color: green;">{{name}}</span>'
    }
);

var app = new Vue({
    el: "#app",
    data: {
        person: {
            name: "William"
        },
        component: null
    }
});

// Load the dynamic component
setTimeout(function() {

    app.component = {
        is: 'example-component',
        props: { name: app.person.name }
    }

    // Change the name
    setTimeout(function() {
        app.person.name = "Sarah";
    }, 2000);

}, 2000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
            
    <strong>Standard Component</strong><br />
    <example-component :name="person.name"></example-component><br /><br />

    <div v-if="component">
        <strong>Dynamic Component</strong><br />
        <component :is="component.is" v-bind="component.props"></component>
    </div>

</div>
like image 229
William Avatar asked Apr 18 '19 10:04

William


1 Answers

There's two different objects here, person and component, which are declared in the app component's data property.

It is app.component.props.name which is bound to the component's prop, but you are modifying app.person.name which is separate and not bound to the component.

app.component.props and app.person are two separate object instances, and so modifying one of these object's properties will have no effect on the other.

It's difficult to suggest a suitable solution to your problem because your example is too simple (and a little contrived). What you want will not work so long as you are copying the name value between different objects.

I'd redo all of the code, and perhaps use a computed property instead. But with the least amount of changes you can do this:

app.component = {
  is: 'example-component',
  props: {
    get name() { return app.person.name; }
  }
}

Now app.component.props.name is actually a getter function which returns app.person.name. Vue can observe this, and will react when app.person.name changes.

like image 146
Decade Moon Avatar answered Oct 07 '22 02:10

Decade Moon