djcaesar9114
djcaesar9114

Reputation: 2137

Conditionally import a component in Vue Router

I'd like to conditionally import a component in the vue router. Here is what I have for the moment:

children: [
  {
    path: ':option',
    component: () => import('../components/Option1.vue'),
  },
],

Depending on what :option is, I want to import a different component (Option1.vue, Option2.vue, etc.). I know I could put several children but i actually need the option variable in my parent component (I make tests if the route has an option).

How would it be possible to do that?

Thanks in advance :)

Upvotes: 1

Views: 9448

Answers (3)

LeBridges
LeBridges

Reputation: 1

Oh this is really cool so I can foreach component in componentlist, and then render the components in a custom order for each page 🤩

In theory, you can have a blocks array and load all content in a custom order

let blocks = [
    {'type': 'header', 'blockContent': [headerData]},
    {'type': 'section1', 'blockContent': [headerData]},
    {'type': 'component1', 'blockContent': [componentData]},
    {'type': 'component2', 'blockContent': [componentData]},
    {'type': 'section2', 'blockContent': [headerData]},
    {'type': 'component3', 'blockContent': [componentData]},
    {'type': 'footer', 'blockContent': [footerData]}
]



<div v-for(block in blocks)>
  <div v-if="block.type == 'header'">
    </header-component>
  </div>
  <div v-if="block.type == 'section1'">
    </section1-component>
  </div>
  <div v-if="block.type == 'section2'">
    </section2-component>
  </div>
  <div v-if="block.type == 'component1'">
    </component1-component>
  </div>
  <div v-if="block.type == 'component2'">
    </component2-component>
  </div>
  <div v-if="block.type == 'component3'">
    </component3-component>
  </div>
  <div v-if="block.type == 'footer'">
    </footer-component>
  </div>
</div>

This was already answered so I felt free to rant incase someone needs this idea in the future :)

Upvotes: 0

djcaesar9114
djcaesar9114

Reputation: 2137

Here is something that works in VueJS3:

<template>
  <component :is="userComponent"/>
</template>

<script>
import { defineAsyncComponent } from 'vue';
import { useRoute, useRouter } from 'vue-router';

export default {
  computed: {
    userComponent() {
      const route = useRoute();
      const router = useRouter();

      const components = {
        first: 'Option1',
        second: 'Option2',
        third: 'OtherOption',
        fourth: 'DefaultOption',
      };
      if (components[route.params.option]) {
        return defineAsyncComponent(() => import(`./options/${components[route.params.option]}.vue`));
      }
      router.push({ path: `/rubrique/${route.params.parent}`, replace: true });
      return false;
    },
  },
};
</script>

Source: https://v3-migration.vuejs.org/breaking-changes/async-components.html And it's possible to get an error message like this one for the line with "return": Syntax Error: TypeError: Cannot read property 'range' of null

In that case, it means you probably want to migrate from babel-eslint to @babel/eslint-parser (source: https://babeljs.io/blog/2020/07/13/the-state-of-babel-eslint#the-present)

Upvotes: 1

Dan
Dan

Reputation: 63139

You can create a loader component containing a dynamic component instead of doing conditional routing. In the loader, you'll conditionally lazy load the option component based on the route param. Not only is this easier when routing, you also don't have to manually import anything, and only options that are used will be imported.

Step 1. Route to the option loader component

router

{
  path: ':option',
  component: () => import('../components/OptionLoader.vue'),
}

Step 2. In that option loader template, use a dynamic component which will be determined by a computed called optionComponent:

OptionLoader.vue

<template>
  <component :is="optionComponent" />
</template>

Step 3. Create a computed that lazy loads the current option

OptionLoader.vue

export default {
  computed: {
    optionComponent() {
      return () => import(`@/components/Option${this.$route.params.option}.vue`);
    }
  }
}

This will load the component called "Option5.vue", for example, when the option route param is 5. Now you have a lazy loaded option loader and didn't have to manually import each option.


Edit: OP has now indicated that he's using Vue 3.

Vue 3

For Vue 3, change the computed to use defineAsyncComponent:

OptionsLoader.vue

import { defineAsyncComponent } from "vue";
computed: {
  optionComponent() {
    return defineAsyncComponent(() =>
      import(`@/components/Option${this.$route.params.option}.vue`)
    );
  }
}

Upvotes: 6

Related Questions