Home > Net >  Vue 3 dynamcially load async components
Vue 3 dynamcially load async components

Time:07-14

I want to display a component depending on it's type. Let me explain.

I have multiple components that are similar to each other but depending on the given type they should display something different. With the defineAsyncComponent method I can import the components and use them easiliy. Example:

const CheckboxControl = defineAsyncComponent(
  () => import('@/components/editor/controls/CheckboxControl.vue'),
);

This works fine, but if I do this I have a ton of imported components. I don't want this. My approach is to wrap the defineAsyncComponent inside an arrow function like this:

const loadComponent = async (type: string) =>
  defineAsyncComponent(
    () =>
      import(
        `@/components/editor/controls/${type[0].toUpperCase()}${type.substring(
          1,
        )}Control.vue`
      ),
  );

In the template I am then able to render the component like this <component :is="renderComponent(control.type)" />

But this gives me the following warning:

[Vue warn]: Component is missing template or render function.

Awaiting the defineAsyncComponent method doesn't solve the issue.

What am I doing wrong? How can I dynamically import these components?

Update

Here are all the possibilities that can be inside the control.type attribute:

  • checkbox
  • date
  • email
  • number
  • radio
  • range
  • select
  • textarea
  • text

Update 2

This is my current code that is working:

const CheckboxControl = defineAsyncComponent(
  () => import('@/components/editor/controls/CheckboxControl.vue'),
);

const DateControl = defineAsyncComponent(
  () => import('@/components/editor/controls/DateControl.vue'),
);

const EmailControl = defineAsyncComponent(
  () => import('@/components/editor/controls/EmailControl.vue'),
);

const NumberControl = defineAsyncComponent(
  () => import('@/components/editor/controls/NumberControl.vue'),
);

const RadioControl = defineAsyncComponent(
  () => import('@/components/editor/controls/RadioControl.vue'),
);

const RangeControl = defineAsyncComponent(
  () => import('@/components/editor/controls/RangeControl.vue'),
);

const SelectControl = defineAsyncComponent(
  () => import('@/components/editor/controls/SelectControl.vue'),
);

const TextareaControl = defineAsyncComponent(
  () => import('@/components/editor/controls/TextareaControl.vue'),
);

const TextControl = defineAsyncComponent(
  () => import('@/components/editor/controls/TextControl.vue'),
);

const loadComponent = (type: string) => {
  switch (type) {
    case 'checkbox':
      return CheckboxControl;
    case 'date':
      return DateControl;
    case 'email':
      return EmailControl;
    case 'number':
      return NumberControl;
    case 'radio':
      return RadioControl;
    case 'range':
      return RangeControl;
    case 'select':
      return SelectControl;
    case 'textarea':
      return TextareaControl;
    case 'text':
      return TextControl;
    default:
      // TODO: show error component if type not supported
      break;
  }
};

CodePudding user response:

Thanks @Estus Flask for your help :)

So the problem was that I was trying to import it with the @ alias. I changed my method to this:

const loadComponent = (type: string) =>
  defineAsyncComponent(
    () =>
      import(
        `./controls/${type[0].toUpperCase()}${type.substring(1)}Control.vue`
      ),
  );

This works now.

I don't know why in this case it is not working with the @ alias, because it works when I use it in

const CheckboxControl = defineAsyncComponent(
  () => import('@/components/editor/controls/CheckboxControl.vue'),
);

Maybe someone has an explanation to this?

CodePudding user response:

async forces the function to return promise object, while a component is expected. It should be regular function:

const loadComponent = (type: string) => ...

It's defineAsyncComponent that handles underlying promise from import.

  • Related