Generate Vue.js components from a JSON / JavaScript DOM Structure
At times we need the ability to store a document structure and re-create it. For instance when we create WYSIWYG designer or a rich text editor, we may have to store the document structure in server and re-create the same for editing.
Let us look into the render method of the Vue.js component and use the interface the render method exposes (createElement), to build components from JSON or JavaScript object.
The render function
The Vue.js component provides a low level “render” property. The property is actually a function frequently referred to as createElement. The createElement function takes three arguments
- A tag name
- Properties for the tag
- Children — [VNodes] Array of children
Let’s leverage render method and the createElement function to render a JSON / JavaScript DOM structure.
refer: https://vuejs.org/v2/guide/render-function.html#ad
DOM Structure
We need structure to recursively describe a document. The sketchy specification of the node of the document is given below. Each DOM node has following attributes
- tagName — contains the html tag to render
- properties — contains the attributes and other properties related to the html tag
- children — contains the array of DOM structure
- textNode — contains the textual content of a html tag
A node can contain either array of other nodes or a text node
{
tagName: 'div',
children: [
{
tagName: 'h1',
textNode: 'Great News'
},
{
tagName: 'h3',
textNode: 'YOU CAN CREATE VUE COMPONENTS OUT OF JSON'
},
{
tagName: 'a',
properties: {
attrs: {href: '#'}
},
textNode: 'Vue.js'
}
]
};
Recursive function to create VNode
The Vue.js component’s render method returns a VNode. We have to write a recursive function that takes the DOM structure and createElement function and recursively constructs the entire structure as a VNode representation.
Please note that we have renamed createElement function to a shorter name “h”, it is a common practice to do so, to avoid repeated typing of createElement in your code.
We will call our recursive function createComponent
/**
* Create component is a recursive function that takes a DOM structure
* and a rendering function (of vue.js) and returns a Vuejs component.
*/
const createComponent = (dNode, h) => {
// Handle empty elements and return empty array in case the dNode passed in is empty
if (_.isEmpty(dNode)) {
return [];
}// if the el is array call createComponent for all nodes
if (_.isArray(dNode)) {
return dNode.map((child) => createComponent(child, h))
}let children = [];
if (dNode.children && dNode.children.length > 0) {
dNode.children.forEach((c) => {
if (_.isString(c)) {
children.push(c)
} else {
children.push(createComponent(c, h))
}
});
}
// Need to clone
const properties = _.cloneDeep(dNode.properties)
return h(dNode.tagName, properties, children.length > 0? children : dNode.textNode)
}
The createComponent checks for empty dNode and handles it appropriately. We check if dNode is an array and call createComponent on each element. If dNode has children, each child’s VNode representation is created through recursive call.
Care must be taken to clone the properties. The reason is that, the nodes can be set as a property on the component and they will have watchers attached to them. Vue.js will complain about duplicate watchers.
The Component
Now we will finish with a component that calls createComponent from its render method.
/**
* A sample component uses the recursive createComponent to render a DOM / List of DOM nodes
*/
const MyComponent = Vue.component('my-component', {
render: function (h) {
return createComponent(this.nodes, h)
},
props: {
nodes: {
type: Object,
required: true
}
}
});
Here the MyComponent implements a render method, which uses the createComponent function to create VNodes and render them. The nodes themselves are passed in as property.
An example component that uses MyComponent shown below
new Vue({
el: '#app',
template: '<MyComponent :nodes="nodes"/>',
components: { MyComponent },
data: function(){
return {
nodes: {
tagName: 'div',
children: [
{
tagName: 'h1',
textNode: 'Great News'
},
{
tagName: 'h3',
textNode: 'YOU CAN CREATE VUE COMPONENTS OUT OF JSON'
},
{
tagName: 'a',
properties: {
attrs: {href: '#'}
},
textNode: 'Vue.js'
}
]
}
}
}
});
Result
We have taken a plain JSON / JavaScript node structure and created a HTML document out of it. As mentioned earlier, this will be helpful if we have a serialized version of a document that we need to recreate.
Much more can be done such as attaching events to the created VNodes, styling them etc, I hope you can explore all these further.
The jsbin for the example component can be found at
Thanks for reading along, if you find this useful please like and share. Happy coding.