mcv
mcv

Reputation: 4429

Dynamic components based on route params in vue-router

Is it possible in Vue to dynamically use components based on the value of a route parameter?

Here's an example of what I'm trying to do:

path: '/model/:modelName',
components: {
  default: ModelDefinition.models.find((model) => model.key === $route.params.modelName),
},

The problem is: $route is not available here. Neither is modelName.

What might be nice if default could also be a function (similar to props):

path: '/model/:modelName',
components: {
  default: (modelName) => (ModelDefinition.models.find((model) => model.key === $route.params.modelName)),
},

This function actually gets called to my surprise, but the argument seems to be some sort of function, and certainly not the modelName I'm hoping for.

The reason I need this is: there's actually a lot more configuration for this route, specifying lots of components and values for various things. If I have to specify that for every model, it's going to be long and repetitive, and would have to be expanded for every model we add to our system.

Upvotes: 0

Views: 1808

Answers (2)

theHarsh
theHarsh

Reputation: 680

In components declaration, this is not available and hence you can not access the props and other instance data.

What you can do is create a computed property that returns a component. Like this:

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

<script>
export default {
  props: {
    componentName: String,
  },
  computed: {
    componentInstance() {
      return () => import(`./modules/${this.componentName}/template.vue`);
    },
  },
};
</script>

Upvotes: 1

muka.gergely
muka.gergely

Reputation: 8329

I'm not sure you have to "do magic" with vue-router - why don't you just handle the parameter inside your component (or create a container-component, and handle the parameter there)?

The snippet below shows that you can use external data to control what component should be displayed. If you pass your model parameter down, then any existing (and registered) component-name can be displayed.

const CompA = {
  template: `<div class="border-blue">COMP-A</div>`
}

const CompB = {
  template: `<div class="border-red">COMP-B</div>`
}

new Vue({
  el: "#app",
  components: {
    CompA,
    CompB
  },
  data: {
    selected: ''
  }
})
[class^="border-"] {
  padding: 10px;
}

.border-blue {
  border: 1px solid blue;
}

.border-red {
  border: 1px solid red;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<div id="app">
  <select v-model="selected">
    <option disabled value="">Please select one</option>
    <option>CompA</option>
    <option>CompB</option>
  </select>
  <br />
  <span>Selected: {{ selected }}</span>
  <br />
  <br /> Here's a variable component - based on external data:
  <component :is="selected"></component>
</div>

Upvotes: 1

Related Questions