9

In order to use a dynamically-defined single page component, we use the component tag, thusly:

<component v-bind:is="componentName" :prop="someProperty"/>

...

import DynamicComponent from '@/components/DynamicComponent.vue';

...
components: {
    DynamicComponent
},

props: {
    componentName: String,
    someProperty: null,
}

The problem is, this isn't really very dynamic at all, since every component we could ever possibly want to use here needs to be not only imported statically, but also registered in components.

We tried doing this, in order at least to avoid the need to import everything:

created() {
    import(`@/components/${this.componentName}.vue`);
},

but of course this fails, as it seems that DynamicComponent must be defined before reaching created().

How can we use a component that is truly dynamic, i.e. imported and registered at runtime, given only its name?

Marc
  • 1,812
  • 4
  • 23
  • 36

4 Answers4

7

Solution for Nuxt only

As of now its possible to auto-import components in Nuxt (nuxt/components). If you do so, you have a bunch of components ready to be registered whenever you use them in your vue template e.g.:

<MyComponent some-property="some-value" />

If you want to have truly dynamic components combined with nuxt/components you can make use of the way Nuxt prepares the components automagically. I created a package which enables dynamic components for auto-imported components (you can check it out here: @blokwise/dynamic).

Long story short: with the package you are able to dynamically import your components like this:

<NuxtDynamic :name="componentName" some-property="some-value" />

Where componentName might be 'MyComponent'. The name can either be statically stored in a variable or even be dynamically created through some API call to your backend / CMS.

If you are interested in how the underlying magic works you can checkout this article: Crank up auto import for dynamic Nuxt.js components

5

From the documentation: Emphasis mine

<!-- Component changes when currentTabComponent changes -->
<component v-bind:is="currentTabComponent"></component> 

In the example above, currentTabComponent can contain either:

  • the name of a registered component,
  • or a component’s options object

If currentTabComponent is a data property of your component you can simply import the component definition and directly pass it to the component tag without having to define it on the current template.

Here is an example where the component content will change if you click on the Vue logo.

Like this:

<component :is="dynamic" />

...

setComponentName() {
    this.dynamic = () => import(`@/components/${this.componentName}.vue`);
},
tony19
  • 125,647
  • 18
  • 229
  • 307
James Coyle
  • 9,922
  • 1
  • 40
  • 48
  • wow that just worked! we even see the async component being loaded dynamically from the server. so simple, when you know how! I'll just edit your answer to include the magic bit from your example. – Marc Sep 24 '19 at 11:11
0

you are talking about async components. You simply need to use the following syntax to return the component definition with a promise.

Vue.component('componentName', function (resolve, reject) {
  requestTemplate().then(function (response) {
    // Pass the component definition to the resolve callback
    resolve({
      template: response
    })
  });
})
tony19
  • 125,647
  • 18
  • 229
  • 307
oshell
  • 8,923
  • 1
  • 29
  • 47
  • requestTemplate is not defined. We are using Nuxt, by the way. What is requestTemplate? Quick google search found nothing. – Marc Sep 24 '19 at 09:17
  • Also: I'm assuming this code should sit in created()? Is that correct? – Marc Sep 24 '19 at 09:19
  • no. it is just the component definition. you can write it in an external file and import it or just write it at the top. but the real template is in another file and is only loaded when needed. you simply use it in any template with v-bind:is and as soon as the component is needed, vue will start an ajax request and initialise the component. – oshell Sep 24 '19 at 09:24
  • if you use vue-cli you can use different syntax mentioned in the article I send, which will automatically compile to a version, where the component is loaded async. Otherwise you need to define your own api (requestTemplate function) to return the component. It is your own function, which requests the desired template from your own api. – oshell Sep 24 '19 at 09:28
  • Sorry, I'm not seeing any reference to any vue CLI command there. Oh, and one more thing: We render SSR. Does that change anything? – Marc Sep 24 '19 at 09:30
  • Wait... are you suggesting that this bit that says " template: '
    I am async!
    '" here should be loading the contents of DynamicComponent.vue? In other words, we need to write code to read the file and put its contents here?
    – Marc Sep 24 '19 at 09:34
  • We just tested passing an actual .vue component into template, like "template: ''"... which fails. So it seems all we can do here is pass HTML, not an actual component. Or... I missing something here? – Marc Sep 24 '19 at 09:40
  • I guess I should also mention that we are using single page components (as defined by DynamicComponent.vue in the question), so this hard-coded HTML doesn't help us much. We would need a way to render a component to HTML given its name and prop values. Do you know of any such facility? – Marc Sep 24 '19 at 09:55
0

According to the official Documentation: Starting from v2.13, Nuxt can auto import your components when used in your templates, to activate this feature, set components: true in your configuration