176

I've a dynamic view:

<div id="myview">
  <div :is="currentComponent"></div>
</div>

with an associated Vue instance:

new Vue ({
  data: function () {
    return {
      currentComponent: 'myComponent',
    }
  },
}).$mount('#myview');

This allows me to change my component dynamically.

In my case, I have three different components: myComponent, myComponent1, and myComponent2. And I switch between them like this:

Vue.component('myComponent', {
  template: "<button @click=\"$parent.currentComponent = 'myComponent1'\"></button>"
}

Now, I'd like to pass props to myComponent1.

How can I pass these props when I change the component type to myComponent1?

thanksd
  • 54,176
  • 22
  • 157
  • 150
Titouan56
  • 6,932
  • 11
  • 37
  • 61

6 Answers6

325

To pass props dynamically, you can add the v-bind directive to your dynamic component and pass an object containing your prop names and values:

So your dynamic component would look like this:

<component :is="currentComponent" v-bind="currentProperties"></component>

And in your Vue instance, currentProperties can change based on the current component:

data: function () {
  return {
    currentComponent: 'myComponent',
  }
},
computed: {
  currentProperties: function() {
    if (this.currentComponent === 'myComponent') {
      return { foo: 'bar' }
    }
  }
}   

So now, when the currentComponent is myComponent, it will have a foo property equal to 'bar'. And when it isn't, no properties will be passed.

thanksd
  • 54,176
  • 22
  • 157
  • 150
  • Why this is not working for me? It works for the first component, but after i change the “currentComponent” i get a “e.currentProperties” is undefined on child component. – Ricardo Vigatti Feb 23 '18 at 18:20
  • 3
    @RicardoVigatti, without seeing any of your code, it's pretty hard to know – thanksd Feb 23 '18 at 18:22
  • Hey, If I want to add something in `(here)` . It that possible? – fpilee Sep 26 '18 at 15:44
  • 2
    @FelipeMorales, yep, you'd just need to define a default `` for each component you are dynamically rendering. https://vuejs.org/v2/guide/components-slots.html – thanksd Sep 26 '18 at 15:46
  • 1
    The style guide says that prop names should be as detailed as possible. This way breaks the rule. This is also what I use, but I am looking for a better solution. – Koray Küpe Dec 28 '18 at 15:20
  • 1
    @KorayKüpe you are mis-quoting the style guide here. It says "prop definitions should always be as detailed as possible". The intent of a dynamic component inherently means that prop names and structure will almost certainly be different between components loaded into this structure. – Graham Oct 06 '19 at 16:27
  • Don't forget to register props in the component you are dynamically rendering – MCFreddie777 Jan 11 '20 at 13:22
  • What does the "is" do here? It doesn't seem to be referenced anywhere... – seth Jul 01 '20 at 22:40
  • @seth See https://vuejs.org/v2/guide/components.html#Dynamic-Components – thanksd Jul 02 '20 at 00:19
  • @MCFreddie777 How do you 'register the props in the component you are rendering"? – user1142433 May 04 '21 at 05:15
  • Using computed() is an important aspect here; this made a difference for me. – Kalnode Jun 29 '21 at 20:23
28

You can also do without computed property and inline the object.

<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>

Shown in the docs on V-Bind - https://v2.vuejs.org/v2/api/#v-bind

tony19
  • 125,647
  • 18
  • 229
  • 307
Jquestions
  • 1,650
  • 1
  • 23
  • 30
20

You could build it like...

comp: { component: 'ComponentName', props: { square: true, outlined: true, dense: true }, model: 'form.bar' }
     
<component :is="comp.component" v-bind="{...comp.props}" v-model="comp.model"/>
Kornél Takó
  • 321
  • 3
  • 7
3

I have the same challenge, fixed by the following:

<component :is="currentComponent" v-bind="resetProps"> 
   {{ title }}
</component>

and the script is

export default { 
  …
  props:['title'],
  data() {
    return {
      currentComponent: 'component-name',
    }
  },
  computed: {
    resetProps() {
      return { ...this.$attrs };
    },
}
<div
    :color="'error'"
    :onClick="handleOnclick"
    :title="'Title'"
/>

I'm came from reactjs and I found this solve my issue

2

If you have imported you code through require

var patientDetailsEdit = require('../patient-profile/patient-profile-personal-details-edit')
and initalize the data instance as below

data: function () {
            return {
                currentView: patientDetailsEdit,
            }

you can also reference the component through the name property if you r component has it assigned

currentProperties: function() {
                if (this.currentView.name === 'Personal-Details-Edit') {
                    return { mode: 'create' }
                }
            }
1

When you use the <component> inside a v-for you can change the answer of thanksd as follow:

methods: {
  getCurrentProperties(component) {
    if (component === 'myComponent') {
      return {foo: baz};
    }
  }
},

usage

<div v-for="object in object.items" :key="object._your_id">
  <component :is="object.component" v-bind="getCurrentProperties(object.component)" />
</div>

Let me know if there is an easier way.

Dollique
  • 952
  • 2
  • 10
  • 29
  • FYI, in a `v-for` (any list really), it's more performant to define a `computed` or `data` property that returns all the properties as an array and have your components select them by index. Like `v-bind="currentProperties[object.component]"`. Methods are called much more frequently than computed properties. (in my testing...) – Nathan Goings Feb 28 '23 at 20:58