Working with Dynamic Components in Vue.js

Working with Dynamic Components in Vue.js

This post is more than 2 years old.

I'm currently deep into a project using NativeScript Vue and ran into an interesting problem that - thankfully - had an incredibly simple solution in Vue. While I certainly do not believe Vue is perfect, but the deeper I go the more I appreciate how powerful it is. The problem I ran into dealt with dynamic forms.

The application loads a set of data that represents an array of fields. Each field has a particular type, possible options, possible defaults and more. I knew I could build Vue components to represent these fields, but what I wasn't sure about was how to actually use them in my layout. Turns out there is a perfectly simple way to do this - Dymamic Components.

Basically, instead of adding <my-component> to a layout, you would add <component :is="my-component">. And yeah, that's pretty much it. There's one important aspect though. Now that you know how to create a dynamic component, how would you also dynamically pass data to it?

Turns out, the bind feature supports passing an object and expanding the key/value pairs out as attributes and values. It's as simple as: v-bind="object".

So this may make more sense with an example. Let's start off with a Vue application that has some hard coded data representing field data.

const app = new Vue({
  el:'#app',
  data() {
    return {
      fields:[
        {
          'id':1,
          'label':'Name',
          'type':'textField'
        },
        {
          'id':2,
          'label':'Email',
          'type':'textField'
        },
        {
          'id':3,
          'label':'Movies',
          'type':'selectField',
          'answers':[
            { value:1,label:"Aa" },
            { value:2,label:"Bb" },
            { value:3,label:"Cc" }
          ]
        },
        {
          'id':4,
          'label':'Food',
          'type':'radioField',
          'answers':[
            { value:1,label:"Aa" },
            { value:2,label:"Bb" },
            { value:3,label:"Cc" }
          ]
        },
      ]
    }
  }
})

In the data block, I've got 4 fields defined. Each has a label, a type, and some have answers representing options. To keep things simple I kept out things like defaults. In the real app, I'll not only have defaults, but concepts like, "default to the current time." Now let's look at the layout.

<div id="app" v-cloak>
  <component 
             v-for="field in fields" 
             :key="field.id"
             :is="field.type"
             v-bind="field">
  </component>
</div>

Nice and simple. I'm looping over each field, defining it's component based on the field.type value. I then pass in all the data of the field using the v-bind trick described above. As for how I implemented the components, I just did some basic HTML. Here are all three:

Vue.component('textField', {
  template:'<div><input type="text" :id="id"></div>',
  data() {
    return {
    }
  },
  computed:{
  },
  props:["label","id"]
});

Vue.component('selectField', {
  template:
`<div>

<select :id="id">
  <option v-for="answer in answers" :key="answer.value" :value="answer.value">
    
  </option>
</select>
</div>`,
  data() {
    return {
    }
  },
  computed:{
  },
  props:["label","id","answers"]
});

Vue.component('radioField', {
  template:
`<div>
  <br/>
  <div v-for="answer in answers" :key="answer.value">
   <input type="radio" :name="id" :value="answer.value" />
  </div>
</div>`,
  data() {
    return {
    }
  },
  computed:{
  },
  props:["label","id","answers"]
});

This is a pretty ugly implementation of the fields but it gets the job done. And of course in my 'real' app I'll be using both Single File Components and NativeScript components, but you get the idea.

If you want to see a live example of this, check out the CodePen below. Remember this is a super basic implementation for me to test the idea out, nothing more.

See the Pen pxXowY by Raymond Camden (@cfjedimaster) on CodePen.

Header photo by Mark Duffel on Unsplash

Raymond Camden's Picture

About Raymond Camden

Raymond is a developer advocate for HERE Technologies. He focuses on JavaScript, serverless and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Tarık posted on 4/5/2019 at 11:00 AM

Hi, How can we get the values?

Comment 2 (In reply to #1) by Raymond Camden posted on 4/5/2019 at 2:18 PM

You mean values passed to the components? The same you do in any other component.

Comment 3 (In reply to #2) by Tarık posted on 4/22/2019 at 6:01 PM

How can I reach the values we entered when submitting the form. Can I assign these values to the model we have defined?
such as:

const app = new Vue({
el:'#app',
data() {
return {
fields:[...],
values:[]
}
},
methods:{
submit(){
this.values = // Get all values
}
}
})

Comment 4 (In reply to #3) by Raymond Camden posted on 4/22/2019 at 6:24 PM

In your component, you can do @change to run an method that will emit the new value. Let me mock up an example.

Comment 5 (In reply to #3) by Raymond Camden posted on 4/23/2019 at 1:03 PM

Check out this version - https://codepen.io/cfjedima.... Basically I v-model to a value field in the fields array (only the first two have that), and then emit an input event so the v-model knows when the value has changed.

Comment 6 (In reply to #4) by Tarık posted on 4/25/2019 at 12:51 PM

Thanks for the help.

Comment 7 by Anil posted on 7/23/2019 at 12:53 AM

Hi Raymond,

I am amazed to look it working in my real form in one of my project. Thanks. But I do not see a way an don't know how to show pre selected values in select box and radios. I mean typically when user select field and saves value and reservists the form for editing it the other day. How can I show the values one had selected earlier.

Say for example user inputs the following:

name: ray
email: ray2@email
Movies: Bb
Food: Cc

Then How to Show a form populated by the earlier selected fields.
Thanks in advance.

Regards
Anil

Comment 8 (In reply to #7) by Raymond Camden posted on 7/26/2019 at 6:49 PM

I got close. The only thing not working is the INITIAL value for the radio selection. After you select it seems to bind fine.

https://codepen.io/cfjedima...

Based on this doc - https://vuejs.org/v2/guide/...

Comment 9 (In reply to #8) by Anil posted on 7/27/2019 at 9:49 AM

Now, this is working perfectly well!

https://codepen.io/mtech200...

Comment 10 (In reply to #9) by Raymond Camden posted on 7/29/2019 at 12:49 PM

Woot! Great job!

Comment 11 by jason b posted on 10/28/2019 at 9:02 AM

This is awesome, thank-you so much for sharing.
Have been scratching my head for hours trying to work out how to dynamically assign a component type while looping through an array ....... discovered your post & BOOM!
Now onto working out how to use compute to with this technique to assign the component based on some conditional logic ;)

Comment 12 (In reply to #11) by Raymond Camden posted on 10/28/2019 at 1:53 PM

Glad it helped you!