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
Archived Comments
Hi, How can we get the values?
You mean values passed to the components? The same you do in any other component.
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
}
}
})
In your component, you can do @change to run an method that will emit the new value. Let me mock up an example.
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.
Thanks for the help.
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
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/...
Now, this is working perfectly well!
https://codepen.io/mtech200...
Woot! Great job!
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 ;)
Glad it helped you!