pls, am trying to add some components dynamically in vue so that i can easily create some tabs, the components are all stored in an array, but when i looped through each of those components, it displayed the name of the components instead of the content of the components, below is my code
<template>
<v-card>
<v-tabs color="#4FC3F7" slider-color="#004D40" right grow>
<v-tab ripple v-for="(ttab, index) in tabss" :key="index">{{ttab}}</v-tab>
<v-tab-item v-for="(tabCont, index) in tabConts" :key="index">
{{tabCont}}
</v-tab-item>
</v-tabs>
</v-card>
</template>
<script>
import ProfileComponents from './Profile.vue'
import PasswordsComponents from './Passwords.vue'
import ProjectsComponents from './Projects.vue'
import FiniancialsComponents from './Finiancials.vue'
import VerificationsComponents from './Verifications.vue'
export default {
data() {
return {
tabss:['Profile','Passwords','Projects','Finiancials','Verifications'
],
tabConts:['<ProfileComponents/>','<PasswordsComponents/>','<ProjectsComponents/>','<FiniancialsComponents/>','<VerificationsComponents/>'
],
};
},
components:{
ProfileComponents, PasswordsComponents, ProjectsComponents, FiniancialsComponents, VerificationsComponents
}
}
</script>
pls what am i doing wrong
For starters, tabConts is just an array of strings, so you're getting what you are asking for.
You probably want to use the 'component' component, which lets you specify the name of the component to insert as a property:
<component v-bind:is="componentName"></component>
So your template changes to something like this:
<template>
<v-card>
<v-tabs color="#4FC3F7" slider-color="#004D40" right grow>
<v-tab ripple v-for="(ttab, index) in tabss" :key="index">{{ttab}}</v-tab>
<v-tab-item v-for="(tabCont, index) in tabConts" :key="index">
<component :is="tabCont"></component>
</v-tab-item>
</v-tabs>
</v-card>
</template>
This assumes the components are registering themselves correctly, etc., but this should get you closer to a solution.
Related
I created a FormDialog component which extends v-dialog
FormDialog
<template>
<div>
<v-dialog
v-bind="$props"
:value="dialog"
:transition="false"
scrollable
persistent
no-click-animation
#keydown.ctrl.enter="handleSave"
#keydown.esc="handleCancel"
#click:outside="handleCancel"
>
<v-card>
<v-card-title>
<!-- title -->
</v-card-title>
<v-card-text>
<v-container>
<slot name="form">
missing form slot
</slot>
</v-container>
</v-card-text>
<v-card-actions>
<!-- actions -->
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script>
import { VDialog } from 'vuetify/lib'
export default {
extends: VDialog,
props: {
/* Custom props */
/* Direct binding to value prop doesn't work */
dialog: undefined
},
methods: {
handleSave () {
this.$emit('save')
},
handleCancel () {
this.$emit('cancel)
}
}
}
</script>
As you can see I'm using a prop named dialog binded to v-dialog value prop, because direct value usage doesn't work.
ChildComponent (using value, doesn't work)
<FormDialog
:value="showDialog"
#save="handleSave"
#cancel="handleCancel"
>
<!-- form -->
</FormDialog>
Obviously I removed dialog prop references.
ChildComponent (using dialog, works)
<FormDialog
:dialog="showDialog"
#save="handleSave"
#cancel="handleCancel"
>
<!-- form -->
</FormDialog>
Do anyone knows the answer? I'd like to use value prop directly.
I cannot find one proper example of loading an image with a skeleton loader. I'm using nuxt + Vuetify and I'm trying to use a simple image with a skeleton loader.
Here is my code.
<template>
<v-skeleton-loader v-if="loading" :loading="loading" type="image">
<v-card
v-show="loaded"
class="ma-auto elevation-4"
shaped
color="darkgreen"
width="500"
flat
>
<v-img
src="/products/em-lucky-combo.jpg"
max-width="500"
#load="hasLoaded"
>
</v-img>
</v-card>
</v-skeleton-loader>
</template>
my method
<script>
export default {
methods: {
hasLoaded() {
console.log('Image finished loading')
this.loading = false
this.loaded = true
},
},
}
</script>
But as I said the method never gets called when it is inside the v-card tag or directly in the skeleton loader.
I have tried using #load, I have tried using mounted hooks like below.
<script>
export default {
mounted() {
const readyHandler = () => {
if (document.readyState === 'complete') {
console.log('Document Rendered')
this.loading = false
this.loaded = true
document.removeEventListener('readystatechange', readyHandler)
}
}
document.addEventListener('readystatechange', readyHandler)
readyHandler() // in case the component has been instantiated lately after loading
},
}
</script>
But nothing seems to work properly or elegantly. Is it just not possible? The moment the <v-img> tag is inside the skeleton loader or inside a v-card inside a skeleton loader it never gets rendered no matter what I do. One suggestion was to use a slot. I'm guessing it has something to do with slots but I do not understand how to use these.
I tried doing something like this.
<template v-slot:default>
<v-card
class="ma-auto elevation-4"
shaped
color="darkgreen"
width="500"
transition="fade-transition"
flat
>
<v-img
src="/products/em-lucky-combo.jpg"
max-width="500"
transition="fade-transition"
#load="imageLoaded"
></v-img>
</v-card>
</template>
I tried using the code below.
How to make v-skeleton loader inside v-for in Vuetify
<v-img>
<template #placeholder>
<v-sheet>
<v-skeleton-loader />
</v-sheet>
</template>
</v-img>
At the end, OP had enough of using just some dimensions and a lazy loader image. Since images were local, there was no need for a skeleton.
<template>
<v-card class="ma-auto elevation-4" flat shaped width="500">
<v-img
aspect-ratio="1"
class="grey lighten-2"
lazy-src=""
max-height="350"
max-width="500"
src="/"
transition="fade-transition"
>
<template v-slot:placeholder>
<v-row
align="center"
class="fill-height ma-0"
justify="center"
>
<v-progress-circular
color="grey lighten-5"
indeterminate
></v-progress-circular>
</v-row>
</template>
</v-img>
</v-card>
</template>
Thanks to #kissu I realised my approach was wrong and unnecessary . I ended up giving my images some set dimensions and using a lazy image loader with a placeholder slot which works just fine. As he mentioned my images are loaded locally so it wont really work in my case.
What I did in the end.
<template>
<v-card class="ma-auto elevation-4" flat shaped width="500">
<v-img
aspect-ratio="1"
class="grey lighten-2"
lazy-src=""
max-height="350"
max-width="500"
src="/"
transition="fade-transition"
>
<template v-slot:placeholder>
<v-row
align="center"
class="fill-height ma-0"
justify="center"
>
<v-progress-circular
color="grey lighten-5"
indeterminate
></v-progress-circular>
</v-row>
</template>
</v-img>
</v-card>
</template>
I'm working with vue-howler and for some reason, the data there is being passed using mixin, I don't see a way to access such mixin from the vue file script (much less do I find a way to change the data in it)
I want to show the progress of playing a music file in a vuetify slider, but due to being contained in a mixin, i get an error "Computed property was assigned to but it has no setter."
//MusicPlayerComponent.vue
<template>
<v-card flat width="500px" height="200px">
<v-flex class="info d-flex justify-center">
<v-btn icon large #click="togglePlayback">
<v-icon>
{{ playing ? 'mdi-pause' : 'mdi-play' }}
</v-icon>
</v-btn>
<v-slider v-model="progress" min="0" max="100">
</v-slider>
</v-card>
</template>
I don't need to show the script section of my vue file because there is literally nothing there, the "progress, playing, togglePlayback" properties and functions in the above code all come from that "mixin"
//MusicPlayerComponent.vue
<script lang="ts">
import { Component, Vue, Watch} from "vue-property-decorator"
// #ts-ignore
import VueHowler from 'vue-howler'
#Component({
mixins: [VueHowler]
})
export default class AudioPlayerComponent extends Vue {
}
</script>
You can use the seek property of vue-howler to get/set the position of playback for a sound. See docs for more props and options
I use vuetify https://vuetifyjs.com/en/
My parent component like this :
<template>
<v-container>
...
<v-col
v-for="(item, i) in items"
:key="i"
>
<v-card
>
<v-app-bar dark color="grey">
<v-toolbar-title>Weekly Schedule : {{item.name}}</v-toolbar-title>
<div class="flex-grow-1"></div>
<modal-datepicker :schedule="item" />
</v-app-bar>
</v-col>
...
</v-container>
</template>
<script>
import { mapState } from "vuex";
import modalDatepicker from "../views/modalDatepicker";
export default {
components: {modalDatepicker},
};
</script>
My child component like this :
<template>
<v-dialog
:ref="dialog"
v-model="modal"
:return-value.sync="date"
>
<template v-slot:activator="{ on }">
<v-btn color="success" dark v-on="on">Show datepicker</v-btn>
</template>
<v-row justify="center">
<v-date-picker v-model="date" scrollable>
<div class="flex-grow-1"></div>
<v-btn text color="primary" #click="modal = false">Cancel</v-btn>
<v-btn text color="primary">OK</v-btn>
</v-date-picker>
</v-row>
</v-dialog>
</template>
<script>
import { mapState } from "vuex";
import modalDatepicker from "../views/modalDatepicker";
export default {
components: {modalDatepicker},
props: ['schedule'],
data: () => ({
date: new Date().toISOString().substr(0, 10),
modal: false,
}),
mounted() {
console.log(this.schedule)
},
};
</script>
The code above works, but seems inefficient. because every time the loop, it call modal dialog. I want modal to only be called when user click show datepicker button
how do i do that?
Apologies as I am on my mobile. Have you considered having the modal in your parent component outside of the loop?
You could have a data variable to handle your modal e.g
modal: {
open: false,
schedule: null }
Essentially then when your button is clicked you could add a v-click with a function that controls this data which in turns handles the content in the modal.
Then on your date picker or modal have a function to handle the update/close to clear this data and handle whatever you need too outside.
Then you could combine this into one component.
I see you have vuex you could also use vuex to control your modal and it’s contents.
Is it possible to create parent (wrapper) components in Vue? I've looked and I haven't been able to find anything
What I have in mind is the following (the v-something components are from the vuetify library):
//cardWrapper.js
<template>
<v-card>
<v-row>
<v-col>
</v-col>
</v-row>
</v-card>
<template>
<script>
export default {
blabla
}
</script>
then this should somehow be available so that I can in the main file
// index.vue
<template>
<cardWrapper>
<v-btn>Click Me!</v-btn>
</cardWrapper>
</template>
I'm guessing this is a straightforward process and that I simply haven't been googling it correctly.
The only thing I've been able to find was to use dynamic components, but I would rather not pass components as properties
Here's the answer:
custom component
//custom-parent.vue
<template>
<custom-parent>
<slot/>
</custom-parent>
</template>
main file
//index.vue
<template>
<custom-parent>
<a>Hellohello</a>
</custom-parent>
</template>