Add component to DOM programatically

I would like to be able to add and remove components from the DOM dynamically using Javascript. Is this possible?

How would you inject a component into a template this way?

The use case for this is to build a dynamic landing page builder using Vue. Ideally without resorting to using something like jQuery for DOM manipulation.

Thank you for any tips.

–harris

2 Likes
const Hello = {
  props: ['text'],
  template: '<div>{{ text }}</div>',
};

// create component constructor
const HelloCtor = Vue.extend(Hello);
const vm = new HelloCtor({
  propsData: {
    text: 'HI :)'
  }
}).$mount('#mount');

LIVE - https://jsfiddle.net/4fn2h4vL/

2 Likes

Really cool. Thank you. The $mount function looks like the key here that I was missing.

I wonder if it is possible to do this with a self contained component that lives in a .vue file?

Seems like if the component is imported you could just do

new ComponentNameHere({props: {...}}).$mount('#elementIdHere');

Where ComponentNameHere lives in ComponentNameHere.vue. Does that sound reasonable?

–harris

1 Like

the object your get from a .vue file is only a normal object with Component options - its not a constructor.

You can do this:

import Component from './component.vue'
const ComponentCtor = Vue.extend(Component)
const componentInstance = new ComponentCtor({ propsData: { ... } })
componentinstance.$mount('#elementidhere')

or, easier:

import Component from './component.vue'
const componentInstance = new Vue({ 
  ...Component, // stage-2 rest spread operator
  propsData: { ... }
})

// ES5/6 compatible:
const componentInstance = new Vue( Object.assign({}, Component, {
  propsData: { ... }
})

But I would generally only use this in extreme scenarios. First you should make sure that a dynamic component can’t achieve what you want:

6 Likes

Really cool. Is there an opposite function from $mount that can remove a component from the DOM?

–harris

$destroy, see the docs.

Vue 1 had $destroy( true ) that would also remove it from the DOM.

How do you remove the component from the DOM in Vue2? (the boolean flag has been removed).

with the normal DOM API methods.

Excellent, I was just looking for this answer. :slight_smile:

Any reason that the Vue API docs say that the propsData property is mainly for unit tests? Isn’t the method that you describe here suited for production?

/Joachim

Totally fine for production. It’s “mainly for unit tests” because manually mounting components is not a commonly required scenario.

In most of these cases, a dynamic component does the problem equally well, and you don’t have to take care of updates and the lifecycle manually.

1 Like

So just to confirm, this is how one would manually destroy the component nowadays:

vm.$destroy() // cleanup in component
vm.$el.remove() // remove from DOM

EDIT: Oops! That will remove the element that we mounted the Vue component too. In some cases, we might want to keep that Element.

In my case, I am doing this:

vm.$destroy() // cleanup in component
vm.$el.innerHTML = '' // remove inner content
vm = null // I drop the reference so it can be GC'd

The reason I am doing this is because I have a router for switching the content of my div#app element, and different routes can render in different way (with React, with Vue, etc). So I need to keep the container element, and only delete the content that Vue generated, then if the next route is a React route then it will re-use the same container to render React stuff inside.

2 Likes

Ah, shoot. Even my second suggestion doesn’t work for me, because Vue removes the target element and replaces it with the content of the <template>.

In my case, I am mounting my Vue into a Custom Element called <motor-node>, .Vue doesn’t recognize that component, and simply deletes it from DOM.

So far, it’s been a hassle trying to switch between React and Vue in the same element container. React plays well, Vue doesn’t.

2 Likes

Alright, this is the fully working solution for my case, in order to keep the outer container alive without Vue destroying it:

I have this HTML

<div id="app"></div>

I want to render the Vue component inside of the div#app container element, so I make a new element inside the container to mount the Vue component to:

    // create an ephemeral div to mount the component into
    const vueContainer = document.createElement('div')
    // add it to `div#app`
    container.appendChild(vueContainer)
    // mount the Vue component to the ephemeral div, which Vue will remove from DOM
    vm = ( new ( Vue.extend(App) ) ).$mount(vueContainer)

Then later when I want to unmount it I just destroy like my first suggestion:

    vueInstance.$destroy()
    vueInstance.$el.remove()
    vueInstance = null

Now, div#app still exists in DOM, so I can go ahead and mount something else into it like a React component, a Meteor Blaze component, a Preact component, etc.

4 Likes

Use methods “Vue.compile” and “render”,
Eg:
{
data() {
return {
template: <div></div>
}
},
render: function (createElement, context) {
const self = this;
const args = arguments;
createElement(
‘div’,
{
class: “”,
style: {
width: ‘500px’,
}
},
[
Vue.compile(self.template)
.render
.apply(self, args)
]
}
}

You’re missing a closing parens on your createElement method.

Thanks,I hope this mistake will not make you misunderstanding.

Well I was trying to understand your code but with the missing parenthesis I can’t understand it. Thanks.