|

How to return a component/component-tag dynamically in vue/nuxt?

Question Description:

0

I am trying to convert a next.js app(https://medium.com/strapi/how-to-create-pages-on-the-fly-with-dynamic-zone-8eebe64a2e1) to a nuxt.js app. In this app I can fetch data from an API and the next.js app uses the APIs data to generate new pages with its corresponding content. Works well in Next.js.

The data/content from the API consists of Seo data for the page, some static values and very important of blocks. These blocks have an attribute called __component where the components name is saved and also have the components data like images, text, etc. So I only have to deal with next.js when adding new components.

In next.js I used the catch-all-route ./pages/[[…slug]].js to catch any slug an user may enter. Then the API is fired with the slug from the context.query and I get back the pages data if it exists. Now the APIs json data only needs to be passed to the blockmanager component.

const Universals = ({blocks}) => {
return <div><BlockManager blocks={blocks}></BlockManager></div>;
};

Here the blockmanager gets the json list of blocks, from which to parse the components.

import Hero from '../../blocks/Hero';
import Pricing from '../../blocks/Pricing';

const getBlockComponent = ({__component, ...rest}, index) => {
    let Block;

    switch (__component) {
        case 'blocks.hero':
            Block = Hero;
            break;
        case "blocks.prices":
            Block = Pricing;
            break;
    }

    return Block ? <Block key={`index-${index}`} {...rest}/> : null;
};

const BlockManager = ({  blocks  }) => {
    return <div> {blocks.map(getBlockComponent)} </div>;
};

BlockManager.defaultProps = {
    blocks: [],
};

export default BlockManager;

How can I replicate this line now in nuxt js?

return Block ? <Block key={`index-${index}`} {...rest}/> : null;

How to return a component/component-tag dynamically in vue/nuxt ? Is there maybe another solution to automatically insert the wanted component? Maybe someones know ho to convert the blockmanagers logic to vue/nuxt logic entirely.

Expert Answer

your component looks like it:

<component
  :is="__component"
  key={`index-${index}`}
/>

Read this article to get an idea about it.

Dynamic & Async Components

This page assumes you’ve already read the Components Basics. Read that first if you are new to components.

keep-alive with Dynamic Components

Earlier, we used the is attribute to switch between components in a tabbed interface:

<component v-bind:is="currentTabComponent"></component>

When switching between these components though, you’ll sometimes want to maintain their state or avoid re-rendering for performance reasons. For example, when expanding our tabbed interface a little: PostsArchive

  • Cat Ipsum
  • Hipster Ipsum
  • Cupcake Ipsum

Click on a blog title to the left to view it.

You’ll notice that if you select a post, switch to the Archive tab, then switch back to Posts, it’s no longer showing the post you selected. That’s because each time you switch to a new tab, Vue creates a new instance of the currentTabComponent.

Recreating dynamic components is normally useful behavior, but in this case, we’d really like those tab component instances to be cached once they’re created for the first time. To solve this problem, we can wrap our dynamic component with a <keep-alive> element:

<!-- Inactive components will be cached! -->
<keep-alive>
  <component v-bind:is="currentTabComponent"></component>
</keep-alive>

Check out the result below: PostsArchive

  • Cat Ipsum
  • Hipster Ipsum
  • Cupcake Ipsum

Click on a blog title to the left to view it.

Now the Posts tab maintains its state (the selected post) even when it’s not rendered. See this example for the complete code.

Note that <keep-alive> requires the components being switched between to all have names, either using the name option on a component, or through local/global registration.

Check out more details on <keep-alive> in the API reference.

Async Components

Watch a free video lesson on Vue School

In large applications, we may need to divide the app into smaller chunks and only load a component from the server when it’s needed. To make that easier, Vue allows you to define your component as a factory function that asynchronously resolves your component definition. Vue will only trigger the factory function when the component needs to be rendered and will cache the result for future re-renders. For example:

Vue.component('async-example', function (resolve, reject) {
  setTimeout(function () {
    // Pass the component definition to the resolve callback
    resolve({
      template: '<div>I am async!</div>'
    })
  }, 1000)
})

As you can see, the factory function receives a resolve callback, which should be called when you have retrieved your component definition from the server. You can also call reject(reason) to indicate the load has failed. The setTimeout here is for demonstration; how to retrieve the component is up to you. One recommended approach is to use async components together with Webpack’s code-splitting feature:

Vue.component('async-webpack-example', function (resolve) {
  // This special require syntax will instruct Webpack to
  // automatically split your built code into bundles which
  // are loaded over Ajax requests.
  require(['./my-async-component'], resolve)
})

You can also return a Promise in the factory function, so with Webpack 2 and ES2015 syntax you can make use of dynamic imports:

Vue.component(
  'async-webpack-example',
  // A dynamic import returns a Promise.
  () => import('./my-async-component')
)

When using local registration, you can also directly provide a function that returns a Promise:

new Vue({
  // ...
  components: {
    'my-component': () => import('./my-async-component')
  }
})

If you’re a Browserify user that would like to use async components, its creator has unfortunately made it clear that async loading “is not something that Browserify will ever support.” Officially, at least. The Browserify community has found some workarounds, which may be helpful for existing and complex applications. For all other scenarios, we recommend using Webpack for built-in, first-class async support.

Handling Loading State

New in 2.3.0+

The async component factory can also return an object of the following format:

const AsyncComponent = () => ({
  // The component to load (should be a Promise)
  component: import('./MyComponent.vue'),
  // A component to use while the async component is loading
  loading: LoadingComponent,
  // A component to use if the load fails
  error: ErrorComponent,
  // Delay before showing the loading component. Default: 200ms.
  delay: 200,
  // The error component will be displayed if a timeout is
  // provided and exceeded. Default: Infinity.
  timeout: 3000
})

Similar Posts