I'm building a generic table component on VUE, and pass by props the reference source.
Example:
<drk-table table="users"></drk-table>
After this, I use the Axios to load the data from my server in the mounted() event.
This works fine when I have only one component in my template.
But if I try to reuse the component with different props in a conditional v-if, only the first table its load:
<div v-if="currentPage == 0">
<drk-table table="users"></drk-table>
</div>
<div v-else-if="currentPage == 1">
<drk-table table="admins"></drk-table>
</div>
*(currentPage is a data field)*
However if I do the same with v-show it's works:
<div v-show="currentPage == 0">
<drk-table table="users"></drk-table>
</div>
<div v-show="currentPage == 1">
<drk-table table="admins"></drk-table>
</div>
<template>
<div>
<v-app-bar app clipped-left color="amber">
</v-app-bar>
<v-navigation-drawer v-model="drawer" app clipped color="grey lighten-4">
</v-navigation-drawer>
<v-content>
<v-container fluid fill-height>
<v-layout align-center justify-center>
<v-flex text-xs-center>
<div v-if="currentPage == 0">
<drk-table table="users"></drk-table>
</div>
<div v-else-if="currentPage == 1">
<drk-table table="admins"></drk-table>
</div>
</v-flex>
</v-layout>
</v-container>
</v-content>
<v-footer color="indigo" app>
</v-footer>
</div>
</template>
<script>
export default {
data: () => ({
drawer: false,
itens: [
{ icon: 'contacts', text: 'item0' },
{ icon: 'settings', text: 'item1' },
],
currentPage : 0,
}),
methods:{
goPage(item){
console.log("goint to "+item);
this.currentPage = item;
}
}
};
</script>
The official documentation from VUE says that the v-if will recreate the item every time who the condition turns TRUE.
My doubt about this is why the v-if doesn't work properly when I use the same component with different props?
In this case, I need to change my mounted() event?
Related
I created an ExpansionPanel component based on Vuetify <v-expansion-panel>. The idea is to have a component completely identical to the Vuetify one, plus a couple of useful features (loading, items counter, etc).
ExpansionPanel
<template>
<v-expansion-panel
v-bind="$props"
v-on="$listeners"
>
<template v-for="(_, name) in $slots">
<template :slot="name">
<slot :name="name"></slot>
</template>
</template>
<template
v-for="(_, name) in $scopedSlots"
#[name]="data"
>
<slot
:name="name"
v-bind="data"
></slot>
</template>
<!-- TODO: $listeners -->
<v-expansion-panel-header v-bind="headerProps">
<!-- TODO: slots -->
<v-row
align="center"
no-gutters
>
<span>{{ heading }}</span>
<v-spacer></v-spacer>
<v-chip
v-if="items !== undefined && !loading"
class="mr-4"
color="primary"
label
small
>
{{ $tc('component.expansionPanel.item', items) }}
</v-chip>
</v-row>
</v-expansion-panel-header>
<v-progress-linear
v-if="loading"
indeterminate
></v-progress-linear>
<!-- TODO: $listeners -->
<v-expansion-panel-content v-bind="contentProps">
<!-- TODO: slots -->
<v-row
v-if="message"
justify="center"
>
<v-col cols="auto">
{{ message }}
</v-col>
</v-row>
<slot
v-else
name="content"
></slot>
</v-expansion-panel-content>
</v-expansion-panel>
</template>
<script>
import { VExpansionPanel } from 'vuetify/lib'
export default {
extends: VExpansionPanel,
props: {
/* Custom props */
heading: String,
items: Number,
loading: Boolean,
headerProps: Object,
contentProps: Object
},
computed: {
message () {
return this.loading
? 'Loading items...'
: this.items !== undefined && !this.items
? 'No data available'
: null
}
}
}
</script>
As you can see I'm only able to pass props to <v-expansion-panel-header> and <v-expansion-panel-content> sub components. I need a way to also use their slots and listeners without conflicting with the <v-expansion-panel> ones, maybe a way to rename them before making them available.
I am trying to replicate this login page layout: Mentor Cruise Login Page
Basically a 1/3 vs 2/3 split and a vertically centered login component.
I'm using my default.vue layout to manage whether or not the navbar/appbar are shown at all. Also use middleware to redirect to the authentication.vue page if no user is present. The logic all works. But the layout has me struggling. The Vuetify documentation is spotty or assumes lots of CSS knowledge I don't have. I tried reading up on CSS flexbox but it does not seem to translate as I would expect or maybe Nuxt is getting in the way with the parent-child relationship handling between layouts, pages and components.
If someone could let me know how I can recreate this layout and where this should go, that would be great!
Here are the elements that should create this top-down:
default.vue layout file:
<template>
<v-app id="youtu.be/dQw4w9WgXcQ">
<template v-if="isLoggedIn">
<Navbar />
<Snackbar />
<Confirm />
</template>
<v-main>
<Nuxt />
</v-main>
<Footer />
</v-app>
</template>
authentication.vue page file
<template>
<v-container id="authentication">
<v-row no-gutters fluid align="center">
<v-col cols="4" class="info">
<v-img
:src="require('~/assets/XXX.png')"
contain
max-height="100"
max-width="100"
></v-img>
</v-col>
<v-col cols="8">
<client-only>
<Login />
</client-only>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
head() {
return {
title: this.title,
}
},
data: () => ({
title: 'Authentication',
}),
}
</script>
<style scoped>
.theme--dark.v-application {
background-color: var(--v-background-base, #121212) !important;
}
.theme--light.v-application {
background-color: var(--v-background-base, white) !important;
}
</style>
Login.vue component
<template>
<v-container>
<v-row justify="center" align="center">
<v-col cols="12" sm="8" md="6">
<v-card>
<v-card-title class="headline">
<span v-if="this.isLoggedIn">Welcome {{ this.displayName }}</span>
</v-card-title>
<v-card-text>
<div v-if="!this.isLoggedIn">
<div id="firebaseui-auth-container"></div>
</div>
<div v-else>
<p>You are logged in with {{ this.email }}.</p>
</div>
</v-card-text>
</v-card>
</v-col>
</v-row>
</v-container>
</template>
<script>
import { createNamespacedHelpers } from 'vuex'
const { mapGetters } = createNamespacedHelpers('user')
export default {
name: 'Login',
mounted() {
//firebaseUi stuff that works
},
computed: {
...mapGetters({
isLoggedIn: 'getIsLoggedIn',
displayName: 'getUserDisplayName',
email: 'getUserEmail',
}),
},
}
</script>
What I have:
What I have (Console):
What I want:
If someone can then tell me how the hell I set the col background color using the vuetify theme config from nuxt.config.js that would be amazing. I tried classes, I tried the color prop..nothing works.
// Edit1: Added console/CSS screenshot as per #kissu's request
// Edit2: Added codepen
Solved this in a hackey way by wrapping the elements contained in the 2 columns in a container again. I DO NOT think this is how you should do it. Probably upsetting the CSS gods. But it works.
<v-container id="authentication" fluid fill-height pa-0>
<v-row no-gutters style="height: 100%" id="authentication-row">
<v-col cols="4" class="primary darken-2">
<v-container fluid fill-height justify-center>
<v-img
:src="require('~/assets/logo.png')"
contain
max-height="100"
max-width="100"
></v-img>
</v-container>
</v-col>
<v-col cols="8">
<v-container fluid fill-height justify-center>
<client-only>
FirebaseUI component
</client-only>
</v-container>
</v-col>
</v-row>
</v-container>
I am trying to show a v-skeleton-loader in Vuetify. I have used v-if and v-else. If the image is not loaded, then it should show the skeleton loader. Otherwise, it should should show the image. This is my code:
<template>
<v-col v-for="option in options" :key="option.id" cols="6">
<v-lazy :options="{ threshold: 0.5 }" min-height="130">
<v-hover v-slot="{ hover }">
<v-card id="options_card" link width="160">
<v-sheet v-if="!images" class="px-3 pt-3 pb-3">
<v-skeleton-loader max-width="300" type="image"></v-skeleton-loader>
</v-sheet>
<v-img
v-else
id="thumbnail"
width="100%"
height="130"
:src="option.thumbnail"
></v-img>
</v-card>
</v-hover>
</v-lazy>
</v-col>
</template>
<script>
export default {
data() {
return {
images: false,
}
},
mounted() {
this.images = true
},
}
</script>
But the v-skeleton-loader is not seen on the screen.
VImage has a placeholder slot that would be used for customizing the loader component to be shown while the image is loading:
<v-img>
<template v-slot:placeholder>
<v-sheet>
<v-skeleton-loader />
</v-sheet>
</template>
</v-img>
demo
<v-img>
<template v-slot:placeholder>
<v-sheet>
<v-skeleton-loader />
</v-sheet>
</template>
</v-img>
my v-navigation-drawer takes up the entire row, so the content goes underneath the drawer. I was trying to research online but didn't find anything about it. I am very new to Vuetify. Please help. If you look at the screenshot the Dashboard must be next to drawer but it goes underneath. I have two components Navbar and Drawer. I render them inside of my App.vue. I attached the code below.
App.vue
<template>
<v-app>
<navbar/>
<drawer/>
<v-content>
<h1>Dashboard</h1>
</v-content>
</v-app>
</template>
<script>
import Drawer from './components/Drawer';
import Navbar from './components/Navbar';
export default {
name: 'App',
components: {
Drawer,
Navbar,
},
data: () => ({
}),
method:() => ({})
};
</script>
<style>
</style>
Navbar.vue
<template>
<div>
<nav>
<v-toolbar class="cyan lighten-1" dark prominent height="65">
<v-toolbar-title class="text-uppercase gray--text">
<span class="font-weight-light">Stock</span>
<span>Dashboard</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn text color="black">
<span>Log Out</span>
<v-icon right>exit_to_app</v-icon>
</v-btn>
</v-toolbar>
</nav>
</div>
</template>
<script>
export default {
data(){
return {
}
}
}
</script>
<style>
</style>
Drawer.vue
<template>
<v-navigation-drawer id="app-drawer" class="cyan lighten-1" dark permanent>
<v-list>
<v-list-tile avatar>
<v-list-tile-avatar color="white">
<v-img :src="require('../assets/bull.svg')" height="70" contain class="cyan darken-5"></v-img>
</v-list-tile-avatar>
</v-list-tile>
<v-list-item
v-for="item in items"
:key="item.title"
link
>
<v-list-item-icon>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
<template v-slot:append>
<div class="pa-2">
<v-btn block>Logout</v-btn>
</div>
</template>
</v-navigation-drawer>
</template>
<script>
export default {
data () {
return {
items: [
{ title: 'Dashboard', icon: 'dashboard' },
{ title: 'Account', icon: 'account_box' },
{ title: 'Admin', icon: 'gavel' },
],
}
},
}
</script>
<style>
</style>
Use "app" in v-navigation-drawer like so :
<v-navigation-drawer id="app-drawer" class="cyan lighten-1" dark permanent app>
Further take a look at https://vuetifyjs.com/en/components/navigation-drawers API and use v-app-bar instead of v-toolbar for the main toolbar of your page.
its me again!
so i have a own component:
<template>
<div class='mynewcomponent'>
<v-layout>
<v-flex xs12 sm6 offset-sm3>
<v-card v-bind:style="{ backgroundColor: this.myColor}">
<!-- Picture
<v-img
src="https://cdn.vuetifyjs.com/images/cards/sunshine.jpg"
height="200px"
>
</v-img>
-->
<v-card-title primary-title>
<div>
<slot name="header">Top western road trips</slot>
<br>
<slot name="TestDesciption">1,000 miles of wonder</slot>
</div>
</v-card-title>
<v-card-actions>
<v-btn flat>Share</v-btn>
<v-btn flat color="purple">Explore</v-btn>
<v-spacer></v-spacer>
<v-btn icon #click="show = !show">
<v-icon>{{ show ? 'keyboard_arrow_down' : 'keyboard_arrow_up' }}</v-icon>
</v-btn>
</v-card-actions>
<v-slide-y-transition>
<v-card-text v-show="show">
I'm a thing. But, like most politicians, he promised more than he could deliver. You won't have time for sleeping, soldier, not with all the bed making you'll be doing. Then we'll go with that data file! Hey, you add a one and two zeros to that or we walk! You're going to do his laundry? I've got to find a way to escape.
</v-card-text>
</v-slide-y-transition>
</v-card>
</v-flex>
</v-layout>
</div>
</template>
<script>
export default {
data: () => ({
show: false,
myColor:'#ffffff'
})
}
</script>
and in my about.vue i load it in a for loop:
<template>
<div class='about'>
<mynewcomponent v-for="(item,index) in 100"/>>
<template v-slot:header>
<h3 style="text-align: left;"><span style="color: #3366ff">ID: 1234</span></h3>
</template>
<template v-slot:TestDesciption>
<h3 style="text-align: left">example shit</h3>
</template>
</mynewcomponent>
</div>
</template>
<script>
import myNewComponent from '#/components/myNewComponent.vue'; // # is an alias to /src
export default {
name: 'about',
components: {
'mynewcomponent': myNewComponent
}
}
</script>
now i want the even and odd Cards in other background color.
i tried everything what google says but whitout success.
i will pass the color if index % 2 == 0 (even or odd)
how can i pass the color in the for loop ?
or can someone tell me a better way to do this?
Thank you
You can create a method to bind the class attribute and pass the index as a parameter. For each line, you can evaluate and return a different class to this element.
You can check am example here
methods:{
spanClass: function(index) {
return {
in: index % 2 === 0,
out: index % 2 !== 0
}
}
li.in {
background-color:red;
}
li.out {
background-color:black;
}
<template>
<div class='about'>
<mynewcomponent v-for="(item,index) in 100"/>>
<template v-slot:header>
<h3 style="text-align: left;"><span :class="spanClass(item)">ID: 1234</span></h3>
</template>
<template v-slot:TestDesciption>
<h3 style="text-align: left">example shit</h3>
</template>
</mynewcomponent>
</div>
</template>