Vue, dynamically import template - javascript

I want to be able to swap out a template for a component based on a data property, or a prop. Is this possible?
This is my idea, however this doesn't work of course.
./index.vue
export default {
props:{
},
template: import(`./Themes/Templates/${this.template}`),
data: function() {
return {
template: 'Default'
}
},
./Themes/Templates/Default.vue
<div>
<p>Default template</p>
</div>
Currently getting this error:
invalid template option:[object Module]

Use component that vue.js provide:
<template>
<component :is="myComp"></component>
</template>
...
// You need to import all the possible components
import CompOne from '/CompOne.vue';
import CompTwo from '/CompTwo.vue'
props:{
myComp: {
default: 'comp-one',
type: String,
},
},
...

Try require("./Themes/Templates/Default.vue")
Update:
In Default.vue:
...
export default {
name: "Default"
}
...
and in index.vue:
...
template: this.temp,
data() {
const { Default } = import("./Themes/Templates/Default.vue");
return {
temp: Default
}
}
...

Related

How to Allow Multiple Exports from a Vue3 Single File Component?

I'm making a Vue3 Single File Component for a custom list. In my single file component, I want to export the main default Vue component, but also the enum declaring what type of list it is:
child:
<template>
<Listbox>
<template #header>
<h5>{{listType}}</h5>
</template>
</Listbox>
</template>
<script lang="ts">
export enum PagesListType {
RecentlyCreated = 'Recently Created',
RecentlyModified = 'Recently Modified',
Starred = 'Starred'
};
export default {
props: {
listType: PagesListType
},
data() {
return {
pages: [],
PagesListType
};
},
};
</script>
The enum only makes sense within the context of this component, so I don't want to put it in some other folder of types. It only relates to the behavior of this list. But when I try to do this in the parent component, it fails:
parent:
<template>
<div>
<PagesList :listType="PagesListType.RecentlyCreated"></PagesList>
<PagesList :listType="PagesListType.RecentlyModified"></PagesList>
<PagesList :listType="PagesListType.Starred"></PagesList>
</div>
</template>
<script lang="ts">
import PagesList, { PagesListType } from './PagesList.vue';
export default {
//parent component details
};
</script>
When I import the named PagesListType enum, it is just undefined. What do I need to do to export the named enum correctly? Thanks!
I opine you need to export enum into a separate file and import it in different files to use it. Where do you put this file depends on you mainly, how you want to structure your project.
For instance, types.ts file in the src folder can define and export the enum like:
export enum PagesListType {
RecentlyCreated = 'Recently Created',
RecentlyModified = 'Recently Modified',
Starred = 'Starred'
}
you can use the enum anywhere by importing it like:
import { PagesListType } from '#/types';
you have to use #/ instead of src/. because of the src folder to # in your TypeScript configuration available in the tsconfig.json file.
I was able to kinda get this to work by not exporting the enum, but adding it as a property to the exported default component:
child:
enum PagesListType {
RecentlyCreated = 'Recently Created',
RecentlyModified = 'Recently Modified',
Starred = 'Starred'
};
export default {
props: {
listType: PagesListType
},
PagesListType,
data() {
return {
pages: [],
PagesListType
};
},
};
parent:
<template>
<div>
<PagesList :listType="created"></PagesList>
<PagesList :listType="modified"></PagesList>
<PagesList :listType="starred"></PagesList>
</div>
</template>
<script lang="ts">
import PagesList from './PagesList.vue';
export default {
computed: {
created() {
return PagesList.PagesListType.RecentlyCreated;
},
modified() {
return PagesList.PagesListType.RecentlyModified;
},
starred() {
return PagesList.PagesListType.Starred;
}
},
//other parent implementation details omitted
};
</script>

Vue.js - Single file component - Unknown custom element

I got a simple single-file component called "MobileApps.vue":
<template>
<div class="grey-bg about-page">
<p>{{ greetings }}</p>
</div>
</template>
<script>
export default {
name: 'Home',
data() {
return {
greetings: "Hello"
}
}
};
</script>
And registering it:
const app = new Vue({
el: '#app',
router,
components: {
Home,
About,
Gas,
Electricity,
Contacts,
Electricity_info,
Gas_info,
MobileApps
},
data: {
},
created() {
},
destroyed() {
},
mounted() {
let mySVGsToInject = document.querySelectorAll('img.inject-me');
SVGInjector(mySVGsToInject);
},
methods: {
}
});
When I'm trying to open the HTML file with <MobileApps> component I'll get an error:
vue.esm.js:628 [Vue warn]: Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option.
But even when I provide a name, nothing happens.
Try with cabeb-case format <mobile-apps> like :
<mobile-apps></mobile-apps>

How to pass data to a component template

Currently I'm defining several component template(s) within a single vue component. I've defined some as strings, however it makes it somewhat incomprehensible if it gets more complex. So instead I'm making it return a separate component as a template. However I'm not sure how to pass data to the component.
This is a sample of the current approach for one of my component templates within my vue component. It's returning the template as a string and renders the html.
progessTemplate: function () {
return {
template: ('progessTemplate', {
template: `
<div id="myProgress" class="pbar">
<div id="myBar" :class="barColor" :style="{'width': width}">
<div id="label" class="barlabel" v-html=width ></div>
</div>
</div>`,
data: function () {
return { data: {} };
},
computed: {
width: function () {
if (this.data.SLA <= 20) {
this.data.SLA += 20;
}
return this.data.SLA + '%';
},
barColor: function(){
if(this.data.SLA > 60 && this.data.SLA <= 80){
return 'bar progressWarning';
}else if(this.data.SLA > 80){
return 'bar progressUrgent';
}
}
}
})
}
},
I'd like to avoid this approach and call a separate file instead.
I import the component into my vue file
import QueryDetailTemplate from '../../ej2/queryDetailTemplate';
and within my main vue file I have this function 'QueryDetailTemplate':
export default{
data(){
return{
data: [...],
QueryDetailTemplate: function(){
return {
template: QueryDetailTemplate,
props:{
test: 'Hello World',
},
};
},//end of QueryDetailTemplate
}//end of data
...
}
In my QueryDetailTemplate.vue this is my code:
<template>
<div>
Heyy {{test}} //undefined
</div>
</template>
<script>
export default{
props: ['test'],
created(){
console.log(this.test); //shows undefined
}
}
</script>
It renders the 'Heyy' that's hardcoded but it doesn't get the 'test' prop.
Appreciate any pointers
I'm not quite sure what you're trying to achieve but...you should be specifying the component as a component like so:
export default{
components: {
QueryDetailTemplate
},
data(){
return{
data: [...],
}
}
OR if you want to import it asynchronously:
import Vue from 'vue'
export default {
methods: {
import() {
Vue.component(componentName, () => import(`./${componentName}.vue`));
}
}
}
And then you can render it in main:
<query-detail-template
test='Hello World'>
</query-detail-template>

Vue.js - Dynamic component import in data and computed properties

I have component 'Page' that should display a component which is retrieved via its props.
I managed to get my component loads when I harcode my component path in my component data like this :
<template>
<div>
<div v-if="includeHeader">
<header>
<fv-header/>
</header>
</div>
<component :is="this.componentDisplayed" />
<div v-if="includeFooter">
<footer>
<fv-complete-footer/>
</footer>
</div>
</div>
</template>
<script>
import Header from '#/components/Header/Header';
import CompleteFooter from '#/components/CompleteFooter/CompleteFooter';
export default {
name: 'Page',
props: {
componentPath: String,
includeHeader: Boolean,
includeFooter: Boolean
},
data() {
componentDisplayed: function () {
const path = '#/components/my_component';
return import(path);
},
},
components: {
'fv-header': Header,
'fv-complete-footer': CompleteFooter,
},
}
</script>
But with the data I cannot refer to my props within my function as this is undefined.
I tried to used computed properties instead of data but I have the error "src lazy?0309:5 Uncaught (in promise) Error: Cannot find module '#/components/my_component'. But the module exists! But maybe not at that time ?
computed: {
componentDisplayed: function () {
const path = `#/components/${this.componentPath}`;
return import(path);
},
},
There must be away to deal with that but I am quite a beginner to vue.js :)
Instead of trying to import the component in your child component, instead import it in the parent component and pass the entire component as a prop.
<template>
<div :is="component" />
</template>
<script>
export default {
name: "page",
props: {
component: {
required: true
}
}
};
</script>
And in the parent
<page :component="component" />
and
import Page from './components/Page';
// and further down
data () {
return {
component: HelloWorld
}
}

Adding properties to a vue component

I'm trying to create a "Dropdown List" component. This component should take in a title and a route. I'm having trouble setting up the javascript for this component within the parent. Here is what I'm working with.
PARENT COMPONENT:
<template>
<UserDropdownList v-for="item in items"></UserDropdownList>
</template>
<script>
import SignUp from "../forms/SignUp";
import SignIn from "../forms/SignIn";
import UserDropdownList from "./UserDropdownList";
import { mixin as clickaway } from 'vue-clickaway';
export default {
mixins: [ clickaway ],
components: {
SignUp,
SignIn,
UserDropdownList
},
data: function () {
return {
items: [
{ title: 'Profile', route: "/profile" },
{ title: 'Users', route: "/users" }
]
}
},
computed: {
isLoggedIn () {
return this.$store.getters.isLoggedIn
},
userName () {
return this.$store.getters.currentUser.userName
},
isDropdownOpen () {
return this.$store.getters.dropdownIsOpen
}
},
methods: {
signOut: function(event) {
this.$store.commit("CLOSE_DROPDOWN");
this.$store.commit("LOGOUT");
this.$router.push('/');
},
openDropdown: function() {
if (event.target.id != "button") {
this.$store.commit("OPEN_DROPDOWN");
}
},
closeDropdown: function() {
this.$store.commit("CLOSE_DROPDOWN");
}
}
}
</script>
USER DROPDOWN LIST
<template>
<li v-on:click="closeDropdown"><router-link to={{ item.route }} id="button">{{ item.title }}</router-link></li>
</template>
<script>
export default {
}
</script>
<style>
</style>
ERRORS:
Property or method "item" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.
Error in render: "TypeError: Cannot read property 'title' of undefined"
Anyone understand what I am doing wrong?
you didn't pass item as a props to UserDropdownList
first, pass item as a prop to UserDropdownList
<template>
<UserDropdownList v-for="item in items" v-bind:item="item"></UserDropdownList>
</template>
then, setup UserDropdownList to receive item prop
export default {
props: ['item']
}

Categories