DEV Community

Raymond Camden
Raymond Camden

Posted on • Originally published at raymondcamden.com on

Working with Dynamic Components in Vue.js

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" }
          ]
        },
      ]
    }
  }
})

Enter fullscreen mode Exit fullscreen mode

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>

Enter fullscreen mode Exit fullscreen mode

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"]
});

Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)