Dynamic components using VueJS
Carlos Rodrigues
Posted on July 26, 2019
First things first, if you're just starting with VueJS this might be a bit too advanced for you, I would strongly recommend read documentation on VueJS, especially components.
This is my first guide, criticism is welcome :)
Preface
Swapping components based on user inputs or even just have a form being set by a json file, is really usefull method to maintain your sanity level low throughout a big project, since usually there's hundreds components/forms/pages/etc, so making a change in code it might cause a ripple effect and break something elsewhere.
Since VueJS will handle all the dirty details of handling DOM, we can focus on solve business problems.
I will cover loading components by name, creating on-the-fly and async components.
Component "magic"
Doing dynamic wouldn't be as easy without <component v-bind:is="dynComponent"></component>
check Dynamic & Async Components for more detailed information.
Basically the components will render a component, async function or by component name.
Loading components by name
Using <component/>
will allow you to access global and also local components by name.
// add some different components globaly
Vue.component("test-comp0", {
template: `<p>comp0</p>`
});
Vue.component("test-comp1", {
template: `<p>comp1</p>`
});
Vue.component("test-comp2", {
template: `<p>comp2</p>`
});
// sample app
new Vue({
el: "#app",
components: {
// add a local component
// check https://vuejs.org/v2/guide/components-registration.html#Component-Names
TestComp3: {
template: `<p>comp3 locally registered component</p>`
}
// you can also add some components from other files, using ES6 import or required.
},
data() {
return {
componentIndex: 0
};
},
computed: {
componentName() {
return "test-comp" + this.componentIndex;
}
},
template: `
<div>
Component: {{componentIndex}} <button @click="componentIndex=(++componentIndex)%4">change</button>
<component :is="componentName"></component>
</div>`
});
Cycle between components is useful, but in the real world you would pass some props to it.
To add props, lets change the component test-comp0
and to the app template.
Vue.component("test-comp0", {
props: ["name"], // please use the object props for production
template: `<p>Hello {{name}}</p>`
});
...
// add name prop
<component :is="componentName" name="pikax"></component>
This will pass the prop name to every component. To solve this we can have a computed property and bind it to the component.
// app becomes
new Vue({
el: "#app",
components: {
// add a local component
// check https://vuejs.org/v2/guide/components-registration.html#Component-Names
TestComp3: {
template: `<p>comp3 locally registered component</p>`
}
// you can also add some components from other files, using ES6 import or required.
},
data() {
return {
componentIndex: 0,
name: "pikax"
};
},
computed: {
componentName() {
return "test-comp" + this.componentIndex;
},
componentProps() {
if (this.componentIndex == 0) {
return {
name: this.name
};
}
return {}; // return empty object
}
},
template: `
<div>
Component: {{componentIndex}} <button @click="componentIndex=(++componentIndex)%4">change</button>
<component :is="componentName" v-bind="componentProps"></component>
</div>`
});
On-the-fly components
On-the-fly components are components we just generate as we need using javascript, this shows how powerful the <component></component>
is, some use case scenarios would be building widgets.
We can generate and test components based on the user input.
NOTE: be really careful with this, this can allow attackers to attack your application, please ensure the source is trusted!
new Vue({
el: "#app",
data() {
return {
componentDefinition: `{ template: "<div>Hello</div>" }`
};
},
computed: {
myComponent() {
return eval(`(${this.componentDefinition})`);
}
},
template: `<div>
<p>Change me</p>
<textarea v-model="componentDefinition" rows="4" cols="50"></textarea>
<component v-if="myComponent" :is="myComponent"></component>
</div>
`
});
You can see as you change the textarea, the component should render straight away.
I don't recommend using this, but I think is a good example on how powerful <component></component>
is.
Importing async Components
This is for me the most usefull use case of the component. I greatly recommend reading the (official guide)[https://vuejs.org/v2/guide/components-dynamic-async.html#Async-Components]
Vue.component(
"async-webpack-example",
// The `import` function returns a Promise.
() => import("./my-async-component")
);
Real world problem
In my last project we had a problem of we wanted to collect user information, but the fields would change depending on the journey, some journey would required email, others email and phone.
The solution was to get the journey definition in a JSON file, each time the user would start an journey we would load that file and load the fields.
The strategy was to use names to load the component dynamically loading the components by name, but we ended up loading all the possible editors in Vue.Component
, this worked... but loading them at the start up means the startup time and app size were much bigger than needed.
Solution
Using a mix of Async, (Dynamic)[https://vuejs.org/v2/guide/components-dynamic-async.html] components and Webpack.
// returning equivalent of webpack : import(name)
const getComponent = async path => {
/* I recomend having an switch with the possible components you will load, this
* will allow you only load specific components.
*/
if (path == 1) {
return async () => {
template: `<p>component 0</p>`;
};
} else {
return async () => {
template: `<p>${path}</p>`;
};
}
};
Vue.component("component-fallback", {
template: `<div>This is not the component you're looking for</div>`
});
new Vue({
el: "#app",
data() {
return {
componentIndex: 0,
component: "component-fallback"
};
},
methods: {
changeComponent() {
const newIndex = ++this.componentIndex;
this.loadComponent(newIndex);
},
// returns the component
loadComponent(name) {
const componentFunc = getComponent(name)
.then(x => {
this.component = x;
})
.catch(e => {
this.component = "component-fallback";
});
}
},
template: `
<div>
Component: {{componentIndex}} <button @click="changeComponent">change</button>
<component :is="component"></component>
</div>
`
});
End
Hope my first article is useful for you, I find fascinating how powerful and flexible <component></component>
is.
If you have any more use cases for <component></component>
let me know on the comments.
This story was first publish at medium.com
Posted on July 26, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 30, 2024
November 30, 2024