{"id":265413,"date":"2018-01-23T07:39:02","date_gmt":"2018-01-23T14:39:02","guid":{"rendered":"http:\/\/css-tricks.com\/?p=265413"},"modified":"2018-01-23T07:39:02","modified_gmt":"2018-01-23T14:39:02","slug":"creating-vue-js-component-instances-programmatically","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/creating-vue-js-component-instances-programmatically\/","title":{"rendered":"Creating Vue.js Component Instances Programmatically"},"content":{"rendered":"

This article assumes basic understanding of Vue.js framework and how to create components in it. If you are new to Vue, then this CSS-Tricks series<\/a> is a good place to start.<\/p>\n

I have been on a Vue.js project that required the ability to create components programmatically. By programmatically<\/em>, I mean you create and insert the components completely from JavaScript, without writing anything in the template. This article aims to illustrate how different aspects of using components in a template, such as instantiation, props passing, slots, mounting, translate to JavaScript code.<\/p>\n

<\/p>\n

Normally if you are working with the recommended Single File Component<\/a><\/strong> style, you would have a Button<\/code> component like so:<\/p>\n

<template>\r\n <button :class=\"type\"><slot \/><\/button>\r\n<\/template>\r\n<script>\r\n export default {\r\n   name: 'Button',\r\n   props: [ 'type' ],\r\n }\r\n<\/script><\/code><\/pre>\n

To use it in another component, all you have to do is import the Button<\/code> component and insert its tag in the template:<\/p>\n

<template>\r\n <Button type=\"primary\">Click me!<\/Button>\r\n<\/template>\r\n<script>\r\n import Button from 'Button.vue'\r\n export default {\r\n   name: 'App',\r\n   components: { Button }\r\n }\r\n<\/script><\/code><\/pre>\n

In my case, I don’t know which component to insert in the template and also where to insert it. This information is only available at runtime. So I needed a way to dynamically create component instance for any passed component and insert it in the DOM, during runtime.<\/p>\n

Creating the Instance<\/h3>\n

The very first idea I had to create a dynamic instance of a given component was to pass it to new<\/code> and it would give me the actual instance. But if you notice carefully the script<\/code> block in any of the above component code, they are all exporting a simple object, and not a class (constructor function). If I do this:<\/p>\n

import Button from 'Button.vue'\r\nvar instance = new Button()<\/code><\/pre>\n

…it fails. We need a class. Or, in simple terms, a constructor function. The way to do that is to pass the component object to Vue.extend<\/code><\/a> to create a subclass of the Vue<\/code> constructor. Now we can create an instance out of it with the new<\/code> keyword:<\/p>\n

import Button from 'Button.vue'\r\nimport Vue from 'vue'\r\nvar ComponentClass = Vue.extend(Button)\r\nvar instance = new ComponentClass()<\/code><\/pre>\n

Hooray! Step 1 cleared! Now we need to insert it in the DOM.<\/p>\n

Inserting in DOM<\/h3>\n

Every Vue instance has a method called $mount<\/code><\/a> on it which mounts the component instance on the element you pass to it (i.e. it replaces the passed element with the component instance). This is not a behavior I wanted. I wanted to insert my component instances inside some DOM element. There is a way to do that too. From the official docs:<\/p>\n

If elementOrSelector argument is not provided, the template will be rendered as an off-document element, and you will have to use native DOM API to insert it into the document yourself.<\/p><\/blockquote>\n

That\u2019s what I did:<\/p>\n

import Button from 'Button.vue'\r\nimport Vue from 'vue'\r\nvar ComponentClass = Vue.extend(Button)\r\nvar instance = new ComponentClass()\r\ninstance.$mount() \/\/ pass nothing\r\nthis.$refs.container.appendChild(instance.$el)<\/code><\/pre>\n

There are a couple of things to note in the code above. <\/p>\n

First, $refs<\/code><\/a> is the recommended way to get reference to a DOM element in Vue.js. You specify an attribute on the DOM element you want to reference (<div ref=\"container\"><\/div><\/code> in this case) and then that element is available on the set key on component’s $refs<\/code> property.<\/p>\n

Second, to get the native DOM element reference from a Vue component instance, you can use the $el<\/code> property.<\/p>\n

Passing Props to the Instance<\/h3>\n

Next, I had to pass some props to my Button<\/code> instance. Specifically, the type<\/code> prop. The Vue<\/code> constructor function accepts an options object that we can use to pass in and initialize related things. For passing props, there is a key called propsData<\/code><\/a> which we can use, like so:<\/p>\n

import Button from 'Button.vue'\r\nimport Vue from 'vue'\r\nvar ComponentClass = Vue.extend(Button)\r\nvar instance = new ComponentClass({\r\n    propsData: { type: 'primary' }\r\n})\r\ninstance.$mount() \/\/ pass nothing\r\nthis.$refs.container.appendChild(instance.$el)<\/code><\/pre>\n

We are almost done, with one final remaining bit. With the normal template method, we used button like: <Button>Click me!<\/Button>;<\/code>. We simply wrote the inner text between the tags and it rendered inside the final button tag with the help of slot<\/code>. But now how do we pass it?<\/p>\n

Setting the Slot<\/h3>\n

If you have used slots in Vue.js, you might be aware that the slots are accessible on any instance on the $slots<\/code> property. And if named slots are not used, then slots are available on $slots.default<\/code> as an array. That is the exact key we’ll modify on the instance to set our button’s inner text. Remember, this needs to be done before mounting the instance. Note that in our case we just put a simple string in the slot because that is all we required. But you can pass more complex DOM to it in form for Virtual nodes or VNode using the createElement<\/code> function. You can read about creating Virtual nodes<\/a> in the Vue.js documentation. Thanks to a Vue core team member, Rahul<\/a>, for suggesting this technique. <\/p>\n

import Button from 'Button.vue'\r\nimport Vue from 'vue'\r\nvar ComponentClass = Vue.extend(Button)\r\nvar instance = new ComponentClass({\r\n    propsData: { type: 'primary' }\r\n})\r\ninstance.$slots.default = [ 'Click me!' ]\r\ninstance.$mount() \/\/ pass nothing\r\nthis.$refs.container.appendChild(instance.$el)<\/code><\/pre>\n

Final Demo<\/h3>\n

View Demo<\/a><\/p>\n

Hope you liked this article. Follow me on Twitter<\/a> where I share more articles and side projects of mine.<\/p>\n","protected":false},"excerpt":{"rendered":"

I have been on a Vue.js project that required the ability to create components programmatically. By programmatically<\/em>, I mean you create and insert the components completely from JavaScript, without writing anything in the template. This article aims to illustrate how different aspects of using components in a template, such as instantiation, props passing, slots, mounting, translates to JavaScript code.<\/p>\n","protected":false},"author":248417,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"c2c_always_allow_admin_comments":false,"footnotes":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":[]},"categories":[4],"tags":[1073,1078],"jetpack_publicize_connections":[],"acf":[],"jetpack_featured_media_url":"","jetpack-related-posts":[],"featured_media_src_url":null,"_links":{"self":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/265413"}],"collection":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/users\/248417"}],"replies":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/comments?post=265413"}],"version-history":[{"count":11,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/265413\/revisions"}],"predecessor-version":[{"id":265446,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/265413\/revisions\/265446"}],"wp:attachment":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media?parent=265413"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/categories?post=265413"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/tags?post=265413"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}