Toggling components depending of the current path in Nuxt - vue.js

My layouts/default.vue looks like this:
<template>
<v-app style="background-color: transparent; color: unset">
<v-main>
<ActHeader></ActHeader>
<Nuxt
v-if="
!$nuxt.isOffline ||
$route.name == 'profile-downloads' ||
$route.name == 'profile-downloads-id'
"
style="min-height: 300px"
/>
<Footer />
</v-main>
</v-app>
</template>
<script>
import Footer from '#/components/Footer.vue'
export default {
components: {
Footer,
},
async fetch() {
await this.$store.dispatch('store/retrieveSettings')
await this.$store.dispatch('store/retrieveMenus')
},
}
</script>
I don't want to show actHeader and Footer components into the first page(/) but in other pages(/about) I want to show these components.
I'm already aware of finding out URL and using watch like this:
watch: {
$route (to, from) {
//false actHeader and Footer Components!
}
}
and it's actually working but I'm looking for a better answer, maybe something more logical.

There is no special magic, use a conditional on each component, no cleaner way of doing otherwise (no need to over-engineer here).
<template>
<div>
<act-header v-if="$route.name !== 'index'"></act-header>
<nuxt />
<footer-comp v-if="$route.name !== 'index'"></footer-comp>
</div>
</template>

Related

Passing props to deactivated component in Vue

I've implemented the Tab feature using "keep-alive", like below, I want to pass "items2" data to the component when the selected currentTabComponent is 'Old', how do i make this work? is there any workaround?
<template>
<div>
<button #click="currentTabComponent = 'New'"> New </button>
<button #click="currentTabComponent = 'Old'"> Old </button>
</div>
<keep-alive>
<component :is="currentTabComponent" :items="currentTabComponent === 'New' ? items : items2"></component>
</keep-alive>
</template>
In the logic, i have,
<script>
export default {
data() {
return {
currentTabComponent: "New",
items:['one','two','three'],
items2:['five','six','seven']
}
}
}
</script>
Even if you use keep-alive props will be passed in the usual way, dynamic or not. So if there is a change in props it should reflect in the subcomponent. keep-alive specifically helps in preserving state changes when the component is not used, and not resetting the state when the component is shown again. But in both cases, props will work fine.
Check the below code:
<div id='component-data'>
<button
v-for="tab in tabs"
v-bind:key="tab"
v-on:click="currentTab = tab">
{{ tab }}
</button>
<keep-alive>
<component v-bind:is="currentTab"
:items="currentTab == 'Bags' ? bags : shirts"
class="tab"></component>
</keep-alive>
</div>
<script>
Vue.component('Bags', {
props: ['items'],
template: "<div>Showing {{ items.toString() }} items in bags.</div>"
});
Vue.component('Shirts', {
props: ['items'],
template: "<div>Showing {{ items.toString() }} items in shirts.</div>"
});
new Vue({
el: "#component-data",
data: {
tabs: ['Bags', 'Shirts'],
currentTab: 'Bags',
bags: ['Bag one', 'Bag two'],
shirts: ['Shirt one', 'Shirt two']
}
});
</script>
You should make sure that the sub-components 'New' and 'Old' have declared the items props in their component definition. Also I hope 'New' and 'Old' are the registered names of the components you are using for tabs.

Vuejs unable to access dom element after mounted() even with this.nextTick. Using chartjs

This is my child element
<template lang="html">
<div class="row">
<div class="col-lg-8 col-md-8 col-sm-12 col-xs-12">
<bar-chart :v-if="this.barChartReadyToBeRendered" :chart-data='null' :height="340"></bar-chart>
</div>
<div class="flex-col-docs col-lg-3">
<div class="column" style="height: 150px">
<div class="col">
<q-select dark stack-label="Show Targets" class="select-notification"
v-model="selectTargetNotification"
:options="this.getTargetChangeOptions"
/>
</div>
<div class="col">
<q-select dark stack-label="Agency" class="select-notification"
v-model="selectOrgNotification"
:options="this.getOrganisationOptions"
/>
</div>
</div>
</div>
</div>
</template>
<script>
import BarChart from '../../components/BarChart'
export default {
components: {
BarChart
},
.
.
/* Other code */
mounted () {
console.log('OUTSIDE MOUNTED')
this.$nextTick(() => {
console.log(this.$el)
let ctx = document.getElementById('bar-chart')
console.log('WWWWWWWWWWWWWWWWWW')
console.log(ctx)
console.log(this.$el)
this.createChart('bar-chart')
})
}
</script>
The bar chart chartjs is
<script>
import { Bar, mixins } from 'vue-chartjs'
const { reactiveProp } = mixins
export default {
extends: Bar,
mixins: [reactiveProp],
props: ['options'],
mounted () {
this.renderChart(this.chartData, this.options)
}
}
</script>
<style>
</style>
In my parent element, the template is
<template>
<q-page padding class="row justify-center">
<div style="width: 80vw; max-width: 100vw;">
<div class="flex-row-docs">
<div class="doc-container">
<q-list no-border>
<div class="row justify-start">
<div class="col-6">
<target-changes-agency></target-changes-agency>
</div>
</div>
<div class="q-mb-md q-mt-md q-headline">Full coverage</div>
<span v-if="!isNewsByIdLoaded" class="row justify-center">
<q-spinner-mat :size="36" style="color: #027be3ff; text-align: justify; margin: 2rem;" />
</span>
<div class="row">
<article-cluster :isNewsByIdLoaded="isNewsByIdLoaded"></article-cluster>
</div>
</q-list>
</div>
</div>
</div>
</q-page>
</template>
I am expecting to console.log(ctx) and console.log(this.$el), however the output of those 2 is null and <!-- --> respectively.
I thought mounted and this.$nextTick() will allow me to have access to the DOM. What am i missing here? please help thank you
Why are you assuming that document.getElementById('bar-chart') would return any element? There is no element with that ID being created. What you're rather looking for is document.getElementsByTagName('bar-chart'), but that will also yield no result, because Vue does not internally create Web Components, but inserts the component's root element in place instead. So, what you can do is give your bar-chart component an id attribute, which will be passed to the root element automatically.
The next issue is that your bar-chart component is only visible when the condition in v-if is truthy. That's probably not the case when the component is first being loaded. In this working minimal example, I simply set v-if="false".
const { Bar, mixins } = VueChartJs
const { reactiveProp } = mixins
const BarChart = Vue.component('bar-chart', {
extends: Bar,
mixins: [reactiveProp],
props: ['options'],
mounted () {
//this.renderChart(this.chartData, this.options)
this.$nextTick(() => {
console.log('mounted bar-chart component:');
console.log(this.$el)
});
}
});
Vue.component('example-component', {
template: `<div><bar-chart v-if="false" id="barchart" chart-data="null" height="340"></bar-chart></div>`,
components: [BarChart],
mounted () {
this.$nextTick(() => {
console.log('mounted child component:');
let ctx = document.getElementById('barchart')
console.log(ctx)
console.log(this.$el)
})
}
});
// create a new Vue instance and mount it to our div element above with the id of app
var vm = new Vue({
el: '#app'
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<script src="https://unpkg.com/vue-chartjs#3.5.0/dist/vue-chartjs.min.js"></script>
<div id="app">
<example-component></example-component>
</div>
(The stack snippet console actually hides the <!-- -->, but you can see it in this codepen. Vue automatically inserts this empty HTML comment as a placeholder for a component that is not currently being displayed.)
The output is actually expected, as the bar-chart component is not being rendered, therefore this.$el (referring to the child component, not the bar-chart component) is empty.
Now here ist the same snippet with v-if="true" on the bar-chart component:
const { Bar, mixins } = VueChartJs
const { reactiveProp } = mixins
const BarChart = Vue.component('bar-chart', {
extends: Bar,
mixins: [reactiveProp],
props: ['options'],
mounted () {
//this.renderChart(this.chartData, this.options)
this.$nextTick(() => {
console.log('mounted bar-chart component:');
console.log(this.$el)
});
}
});
Vue.component('example-component', {
template: `<div><bar-chart v-if="true" id="barchart" chart-data="null" height="340"></bar-chart></div>`,
components: [BarChart],
mounted () {
this.$nextTick(() => {
console.log('mounted child component:');
let ctx = document.getElementById('barchart')
console.log(ctx)
console.log(this.$el)
})
}
});
// create a new Vue instance and mount it to our div element above with the id of app
var vm = new Vue({
el: '#app'
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<script src="https://unpkg.com/vue-chartjs#3.5.0/dist/vue-chartjs.min.js"></script>
<div id="app">
<example-component></example-component>
</div>
As you can see, the logs now return the correct elements, also in the mounted() hook of the bar-chart component.
Of course, you shouldn't use the id attribute in your component if you ever plan to have multiple instances of this component, because it would result in multiple elements having the same ID, which is invalid HTML and might lead to unexpected interferences. So, this was only for demonstration purposes in this minimal example. In your real code, you could use Vue's ref attribute instead, which you can then refer to via this.$refs inside the parent component.
There are two other issues in your code:
You don't need the colon in front of v-if, because it automatically binds to the expression given as its value.
You don't need this. in your expressions, you're in the components context automatically and can simply use the properties' names directly.

How to get $auth value in component outside of registered vue-router component

I have a layout theme/default which has vue-router inside like this
<template>
<div id="app">
<component :is = "layout">
<router-view></router-view>
</component>
</div>
</template>
<script>
const default_layout = "theme";
export default {
computed: {
layout(){
return ( this.$route.meta.layout || default_layout) + '-layout';
}
},
};
</script>
And then the theme layout is like this:
<template>
<div class="app-home">
<nav-bar/>
<div class="container-fluid section">
<div class="left-fixed">
<side-bar/>
</div>
<div class="right-card">
<slot />
</div>
</div>
</div>
</template>
<script>
import NavBar from './Navbar'
import SideBar from './Sidebar'
export default {
data() {
return {
}
},
mounted(){
},
components: {
NavBar,
SideBar
}
}
</script>
Now I have to pass current auth user in Navbar and Sidebar for logout and current user role which can be obtained from vue-auth $auth but only inside router component. Can anybody help it to fix this.
Using vuex I had made a state which I call as computed property and I set whenever User logged in.

Vue: Hide view components conditionally based on URL

My App.vue contains below content:
<template>
<v-app>
<core-toolbar />
<core-drawer />
<core-view />
</v-app>
</template>
But I want to hide <core-toolbar /> and <core-drawer /> when it is routed to login page. I am planning to use v-if to hide them. But how can I check whether the current route is login?
Yes - If you used vue-router, you can use the $route object to verify current URL.
You can log the route object and verify.
I add name to routes so
computed: {
isLogin() {
return this.$route.name === 'Login'
}
}
and
<template>
<v-app>
<core-toolbar v-if="isLogin"/>
<core-drawer v-if="isLogin"/>
<core-view />
</v-app>
</template>
You can get many more values like queries / params -
Read more here Vue Router
You can use $route.name
<core-toolbar v-show="$route.name!=='login'" />
<core-drawer v-show="$route.name!=='login'" />
You can access your route data from your Vue instance
<template>
<v-app>
<core-toolbar />
<core-drawer v-if="!isLogin" />
<core-view v-if="!isLogin"/>
</v-app>
</template>
<script>
export default {
computed: {
isLogin() {
return this.$route.name == 'login'
}
}
}
</script>
Inspect the object this.$route to get the right params you need.
You can name the routes with an id:
const routes = [
{
path: '/login',
name: 'login’,
component: 'login'
},
];
Then you can access this.$route whenever to get information about the current route, even in v-if:
<template>
<v-app>
<core-toolbar v-if="$route.name != 'login'" />
<core-drawer v-if="$route.name != 'login'" />
<core-view />
</v-app>
</template>
you can use javascript to get the path
isLoginPage(){
var path = window.location.pathname.split('/')[1]
if(path == 'login'){
return true
}
return false
}
For future reference in Vue3 you need to do the following
import { useRoute } from "vue-router";
import {computed} from "vue";
Then:
const router= userRouter()
const isLogin= computed(()=>router.name==="Login")

Async components in Vue2

I’d like to have all my routes to show Navbar and Footer except “Login” route - it should contain ONLY Logins component content.
In App.vue (my root component) I have this:
<template>
<router-view v-if="$route.name === 'Login'"></router-view>
<div v-else>
<app-nav></app-nav>
<div class="container">
<transition name="bounceLeft" mode="out-in" appear>
<router-view :key="$route.fullPath"></router-view>
</transition>
</div>
<app-footer></app-footer>
</div>
</template>
<script>
export default
{
components:
{
'AppNav': () => import( "#/components/AppNav.vue" ),
'AppFooter': () => import( "#/components/AppFooter.vue" )
}
}
</script>
<style>
</style>
It works, but as you can see, I want to “lazy load” my AppNav and AppFooter components, so they will be downloaded ONLY when they are needed (when routes name IS NOT ‘Login’). Unfortunately this doesnt work - when I go to Login route, these components are still downloaded from the server.
How can I achieve lazy-loading component in this example?
If you use webpack it will looks like this:
<script>
export default
{
components:
{
'AppNav': () => System.import( "#/components/AppNav.vue" ),
'AppFooter': () => System.import( "#/components/AppFooter.vue" )
}
} </script>
I don't know other way )