innerHTML compilation ( Vue 2 )

Hey guys! I’m trying to dynamically insert vue component in the template, but the problem is that it doesn’t get compiled and renders it as raw html.

For an example:

I have a <custom-component></custom-component>.

And I want to dynamically insert it into the parent component:

ParentComponent:

    <template>
        <div class="row">
            <div class="col-lg-6">
                    <custom-component></custom-component>
                </div>
                <div class="col-lg-6">
                    other content
                </div>
        </div>
    </template>

How to tell Vue to recompile ParentComponent, so that <custom-component></custom-component> will be compiled and rendered correctly?

Can you provide your javascript code?

How do you try to “dynamically insert vue component”?

Is anything about dynamic components and v-if missing to do what you want?

I have a parent component’s template and one part of this template is dynamic and needs to be loaded via Ajax. You can see it down below:

<div class="row">

            <div class="col-lg-6">
                %% component_1 %%
            </div>
            
            <div class="col-lg-6">
                %% component_2 %%
            </div>

        </div>

When I load this markup I’ll have to replace placeholders %% component_1 %% with a particular components and recompile parent component’s template in order for those components to be rendered correctly. For now I have above content loaded correctly and even replace placeholders with an appropriate components, like this:

<div class="row">

    <div class="col-lg-6">
        <custom-component1></custom-component1>
    </div>

    <div class="col-lg-6">
        <custom-component2></custom-component2>
    </div>

</div>

But the problem is that those child components ( custom-component1 and custom-component2 ) doesn’t get compiled.

Here’s my child component: <edit-content :content="content"></edit-content>

Prop content is equal to html string below:

<div class="row">

    <div class="col-lg-6">
        <custom-component1></custom-component1>
    </div>

    <div class="col-lg-6">
        <custom-component2></custom-component2>
    </div>

</div>

I wanna insert it as inner html of some element inside of child component, but cannot get that html string compiled so that my custom-component1 and custom-component2 will be rendered as it should be.

This is how I watch for content prop inside of child component edit-content. Any time content changes it should get recompiled:

watch: {
    content () {
        let res = Vue.compile(this.content);
    },
},

Oh, wow, that’s a strange way of doing things. You will have a very hard time to get that working. Not saying it’s possible, but it’s counter to some fundamental principles of how Vue works.

I assume that you mean, you inserted those elements into the DOM. However, Once a component is running, the template is handled internally by the virtualDOM - Vue is not parsing the DOM anymore. So when you add vue-related markup into the DOM, Vue has no way of seeing and parsing this.

For the moment, I’m not sure I have away of making this work, or find a suitable workaroud. I’ll think about it some.

Here’s how I try to compile html string this.content with vue 2:

watch: {
      content () {
          this.renderFunc = Vue.compile(this.content);
          let compiledContent = this.renderFunc.render();
      },
 },

this.content is equal to the following string:

<div class="row">

    <div class="col-lg-6">
        <custom-component1></custom-component1>
    </div>

    <div class="col-lg-6">
        <custom-component2></custom-component2>
    </div>

</div>

After compilation compiledContent variable is an object of type VNode, I think there’s some way to convert it to normal Html element that can be inserted on the page, that’s what I’m stuck with right now.

there’s a link in doc: vue-docs

Anyways, thanks a lot for your time!

I’m aware of that functionality. However, that’s not that easy depending on what you want to do exactly. for instance, the generated render function expects the also generated staticRenderFns to be present on the component instance (if any were geenrated during the compilation process), but your normal template might have such functions of its own.

Also, render should be bound to your current component instance etc. - it’s not an easy to use API.

a more promising approach would probably be to create a dynamic component on the fly:

computed: {
  dynComponent() {
    const template = this.content ? this.content : '<div>nothing here yet</div>'
    return { 
      template, // use content as template for this component
      props: this.$options.props // re-use current props definitions
    }
}

then we create a dynamic component that uses the object from this computed prop as the component definition, and we pass all props we curently have to that component (so that the template string we received from the server can access the same props as you current component can), but if your child components from the content string don’t use any props, that’s not even necessary.

<component is:="dynComponent" v-bind="$props"/>
4 Likes

That’s an interesting approach, But I will know only at runtime which computed properties my vue component will contain, is there some way to add computed properties let’s say in mounted () lifecycle hook?

By the way, can I modify vue component’s template before vue will compile it, ( maybe in some lifecycle hook before mounted () )?

A BIG thank to You! I’ve used your approach and it worked!!!

1 Like

Save my day! I can load server template now!!