I have a single file component Main.Vue.
I also have three other single file components A.vue, B.vue and C.vue.
I want to be able to show inside Main.Vue a different component each time. What I did was this:
<template>
<div>
<a v-if="isAVisible" ></a>
<b v-if="isBVisible" ></a>
</div>
</template>
<script>
import A from './A.vue';
import B from './B.vue';
...
This works but not exactly what I wanted. I wanted a different file Factory.js, which does the importing of all the components A,B,C,.. And has functions that return my component, which I can use somehow in Main.vue. Here's what I tried Factory.js to look like:
import A from './A.vue';
import B from './B.vue';
function getComponent(){
if (..)
return new A();
else if (..)
return new B();
...
}
This didn't work at all. I want the factory file approach because:
1) I want to split it to different factory files
2) I want to "Attach" data to each component. So I'll have an object that contains the function returning the actual component + some additional data like "name"
Any ideas how to do this?
I have a single file component Main.Vue.
I also have three other single file components A.vue, B.vue and C.vue.
I want to be able to show inside Main.Vue a different component each time. What I did was this:
<template>
<div>
<a v-if="isAVisible" ></a>
<b v-if="isBVisible" ></a>
</div>
</template>
<script>
import A from './A.vue';
import B from './B.vue';
...
This works but not exactly what I wanted. I wanted a different file Factory.js, which does the importing of all the components A,B,C,.. And has functions that return my component, which I can use somehow in Main.vue. Here's what I tried Factory.js to look like:
import A from './A.vue';
import B from './B.vue';
function getComponent(){
if (..)
return new A();
else if (..)
return new B();
...
}
This didn't work at all. I want the factory file approach because:
1) I want to split it to different factory files
2) I want to "Attach" data to each component. So I'll have an object that contains the function returning the actual component + some additional data like "name"
Any ideas how to do this?
Share Improve this question edited Feb 10, 2021 at 0:54 Kalnode 11.3k3 gold badges40 silver badges73 bronze badges asked May 5, 2017 at 7:53 Michael_S_Michael_S_ 5386 gold badges12 silver badges31 bronze badges2 Answers
Reset to default 14Use Vue's Dynamic Components
You could use Dynamic Components to dynamically switch between components. You will need to bind the component definition object to the is
attribute of the component
element – Vue's documentation on this is pretty self explanatory. Below is also a brief example:
<template>
<component :is="activeComponent"></component>
</template>
import componentA from 'component/a';
import componentB from 'component/b';
export default {
components: {
componentA,
componentB,
},
data() {
return {
activeComponent: 'componentA',
},
},
};
You could directly bind the component definition object to the data property itself:
import componentA from 'component/a';
import componentB from 'component/b';
export default {
data() {
return {
activeComponent: componentA,
};
},
};
To switch out components you can programmatically change the value of activeComponent
.
Use a render function
A more powerful way of dynamically mounting components can be achieved using component render functions. To do this we must create our own version of Vue's component
element – we'll call this the ElementProxy
:
import componentA from 'component/a';
import componentB from 'component/b';
export default {
components: {
componentA,
componentB,
},
props: {
type: {
type: String,
required: true,
},
props: {
type: Object,
default: () => ({}),
},
},
render(createElement) {
const { props: attrs } = this;
return createElement(element, { attrs });
},
};
You can now use the ElementProxy
to proxy elements. The additional benefit of this is that you can pass props in as an object which will solve the problem of passing props to dynamic components with differing models.
<template>
<element-proxy :type="activeComponent" :props="props"></element-proxy>
</template>
import ElementProxy from 'components/elementProxy';
export default {
components: {
ElementProxy,
},
data() {
return {
activeComponent: 'componentA',
props: { ... },
};
},
};
Further reading
- Vue's documentation for dynamic components
- Vue's documentation for the render function
- GitHub issue thread for binding props
Yes, you need dynamic components:
<template>
<div>
<component v-bind:is="currentView">
<!-- component changes when vm.currentView changes! -->
</component>
</div>
</template>
<script>
import A from './A.vue';
import B from './B.vue';
export default {
data: {
currentView: 'A'
},
components: {
A,
B,
}
})
then
function getComponent(){
if (..)
this.currentView = 'A';
else if (..)
this.currentView = 'B'
...
}
You can also bind components directly, according to manual:
https://v2.vuejs.org/v2/guide/components.html#Dynamic-Components