Vue: Hide view components conditionally based on URL - vue.js

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")

Related

Vue keeping specific component alive under router-view

In my home page I have two components. I want only one to be kept alive.
HomeView.vue
<template>
<div>
<keep-alive>
<AliveComponent />
</keep-alive>
<DeadComponent />
</div>
</template>
In my App.vue I have kept the <router-view /> alive as well
<template>
<Header />
<main>
<keep-alive include="HomeView">
<router-view />
</keep-alive>
</main>
<Footer />
</template>
The problem is the <keep-alive include="HomeView"> is keeping both components alive. I tried to replace it with include="AliveComponent" but turns out nothing is alive.
Is there any way to do it?
Make sure your components have a valid name that you are planning to use in keep-alive. This could be an issue if you did not provide a name to your "AliveComponent" because the include property matches the component name.
I produced a demo here in which I create two routes. Each route has a component and each component has its state. When changing any component's state and switching to another route, the component which is included in the keep-alive will have preserved state while others will not.
// 1. Define route components.
// These can be imported from other files
const AliveC = {
name: "AliveComponent",
template: `<div>
<button #click="count = count + 1">Increase Alive Count</button>
<h3>Alive Component Count - {{ count }}</h3>
</div>`,
data() {
return {
count: 0,
};
},
}
const DeadC = {
name: "DeadComponent",
template: `<div>
<button #click="count = count + 1">Increase Dead Count</button>
<h3>Dead Component Count - {{ count }}</h3>
</div>`,
data() {
return {
count: 0,
};
},
}
// 2. Define some routes
// Each route should map to a component.
// We'll talk about nested routes later.
const routes = [
{ path: '/', component: AliveC },
{ path: '/dead', component: DeadC },
]
// 3. Create the router instance and pass the `routes` option
// You can pass in additional options here, but let's
// keep it simple for now.
const router = VueRouter.createRouter({
// 4. Provide the history implementation to use. We are using the hash history for simplicity here.
history: VueRouter.createWebHashHistory(),
routes, // short for `routes: routes`
})
// 5. Create and mount the root instance.
const app = Vue.createApp({})
// Make sure to _use_ the router instance to make the
// whole app router-aware.
app.use(router)
app.mount('#app')
// Now the app has started!
<html>
<script src="https://unpkg.com/vue#3"></script>
<script src="https://unpkg.com/vue-router#4"></script>
<div id="app">
<p>
<router-link to="/">Switch To Alive Component Route</router-link>  
<router-link to="/dead">Switch To Dead component Route</router-link>
</p>
<!-- route outlet -->
<!-- component matched by the route will render here -->
<router-view v-slot="{ Component }">
<keep-alive include="AliveComponent">
<component :is="Component" />
</keep-alive>
</router-view>
</div>
</html>

Vue: Reuse loading spinner template and logic

I've multiple (20+) pages where I need the following code:
<template>
<template v-if="isLoading">
<Spinner full size="medium" />
</template>
<template v-else>
<p>Page data</p>
</template>
</template>
<script>
export default {
computed: {
isLoading() {
return this.$store.getters['loader/isLoading'];
},
},
};
</script>
I don't want to rewrite this part everytime I need it so is there a way to create something like a higher order component with access to the computed method and a way to add the hoc to the script tag of the different vue files? Or another way to archive this?
I could recommend extending the spinner component where you want to show the spinner. I've created a boilerplate setup that show a very simple implementation of this approach here.
The main idea is to expose a default slot for you spinner component, and wrap the page component in that slot.
<template>
<div>
<Spinner v-if="isLoading" full size="medium" />
<!-- Slot for component data -->
<slot v-else></slot>
</div>
</template>
<script>
export default {
computed: {
isLoading() {
return this.$store.getters['loader/isLoading'];
},
},
};
</script>
Then in any component that you want to show the spinner:
<template>
<spinner>
<!-- Pass here the component content to be shown after the spinner is hidden -->
</spinner>
</template>
<script>
import Spinner from "./spinner";
export default {
name: "Page1",
extends: Spinner,
components: { Spinner }
};
</script>

How to pass vmodel property to child component in nuxjs

I can't seem to pass dynamically modified properties from layouts into the <Nuxt /> component.
This is my ~/layouts/default.vue
<template>
<div>
<input v-model="myprop" />
<span>{{myprop}}</span>
<Nuxt />
</div>
</template>
<script>
export default {
provide: function () {
return {myprop: this.myprop};
},
data: () => ({
myprop: 'hello galaxy',
}),
}
</script>
This is my ~/pages/index.vue
<template>
<div>My Prop is: {{myprop}}</div>
</template>
<script>
export default {
inject: ["myprop"]
}
</script>
On the web page I see hello galaxy printed 3 times, once in the input, once in a span, and once in the Nuxt component. But when I edit the input field, only the span is updated. The Nuxt component does not capture the changes in myprop. The Nuxt component continues to show only hello galaxy while put the input and span shows changes as I type on my keyboard
What am I doing wrong?
The provide/inject is useful for simple situation, but if you've some reactive stuff the vuex store is more convenient :
in store/index.js
add a state called search and its mutations and actions :
export const state=()=>({
search:''
})
export const mutations ={
SET_SEARCH(state,payload){
state.search=payload
}
}
export const actions ={
setSearch(context,payload){
context.commit('SET_SEARCH',payload) ​
}
}
in layout/default.vue add computed property with setter/getter bound to the store:
<template>
<div>
<input v-model="search" />
<span>{{search}}</span>
<Nuxt />
</div>
</template>
<script>
export default {
computed:{
search:{
get(){
return this.$store.state.search
},
set(val){
this.$store.dispatch('setSearch',val)
}
}
}
}
</script>
in pages/index.vue :
<template>
<div>My search is: {{search}}</div>
</template>
<script>
export default {
computed:{
search(){
return this.$store.state.search
}
}
}
</script>

parent component is not getting data from child in Nuxt app

This is driving me crazy so I hope that anyone can help.
I made a Nuxt app with #nuxt/content and I'm using Netlify-CMS to create content. That all seems to work fine. However I'm trying to display a component that contains a loop of the MD-files that I have, but in the index.vue nothing of the loop is displayed.
I know (a little) about props and $emit, but as I am not triggering an event this dosen't seem to work.
Component code:
<template>
<section>
<h1>Releases</h1>
<li v-for="release of rfhreleases" :key="release.slug">
<h2>{{ release.artist }}</h2>
</li>
</section>
</template>
<script>
export default {
components: {},
async asyncData({ $content, params }) {
const rfhreleases = await $content('releases', params.slug)
.only(['artist'])
.sortBy('createdAt', 'asc')
.fetch()
return {
rfhreleases,
}
},
}
</script>
And index.vue code:
<template>
<div>
<Hero />
<Releases />
<About />
<Contact />
</div>
</template>
<script>
export default {
head() {
return {
script: [
{ src: 'https://identity.netlify.com/v1/netlify-identity-widget.js' },
],
}
},
}
</script>
If I place my component code as part of index.vue, everything work, but I would love to avoid that and thats why I'm trying to place the loop in a component.
As stated on the Nuxt documentation:
This hook can only be placed on page components.
That means asyncData only works on components under pages/ folder.
You have several options:
You use fetch instead. It's the other asynchronous hook but it's called from any component. It won't block the rendering as with asyncData so the component it will instanciated with empty data first.
You fetch your data from the page with asyncData and you pass the result as a prop to your component
<template>
<div>
<Hero />
<Releases :releases="rfhreleases" />
<About />
<Contact />
</div>
</template>
<script>
export default {
async asyncData({ $content, params }) {
const rfhreleases = await $content('releases', params.slug)
.only(['artist'])
.sortBy('createdAt', 'asc')
.fetch()
return {
rfhreleases,
}
},
}
</script>

Dynamic component and conditional rendering in Vue 2

Recently I've played with dynamic components with Vue 2.
Let's say we have two components SignIn and SignOut that must be conditionally rendered.
import SignIn from '~/components/SignIn.vue
import SignUp from '~/components/SignUp.vue
export default {
components: {
SignOut,
SignIn
}
data() {
return {
condition: true
}
}
I usually use the Vue conditional render pattern as follow:
<template>
<SignIn v-if="condition" />
<SignUp v-else />
</template>
But I can achive the same result with dynamic components pattern.
<script>
data() {
return {
condition: true,
component: this.condition ? 'SignIn' : 'SignUp'
}
}
</script>
<template>
<component :is="component" />
</template>
What do you think about it?