Experiencing navbar flicker with Vue.js - vue.js

I'm experiencing a flicker in my navbar before a function is evaluated to either true or false.
The function that needs to evaluate is the following:
export default {
methods: {
isAuthenticated () {
return this.$store.state.user.authenticated
}
},
data: () => {
return {
unauthenticated: [
{
title: 'Link1',
url: '/link1'
},
{
title: 'Link2',
url: '/link2'
},
{
title: 'Link3',
url: '/link3'
}
],
authenticated: [
{
title: 'otherLink1',
url: '/otherlink1'
},
{
title: 'otherLink2',
url: '/otherlink2'
},
{
title: 'otherLink3',
url: '/otherlink3'
}
]
}
}
}
And the navbar has the following:
<template v-if="isAuthenticated()">
<b-nav is-nav-bar>
<b-nav-item v-for="nav in authenticated" :key="nav.title" :href="nav.url">{{nav.title}}</b-nav-item>
</b-nav>
</template>
<template v-else>
<b-nav is-nav-bar>
<b-nav-item v-for="nav in unauthenticated" :key="nav.title" :href="nav.url">{{nav.title}}</b-nav-item>
</b-nav>
</template>
However, when I click through the navigation, the unauthenticated links appear for a second and then the authenticated links appear as if the isAuthenticated() function hasn't evaluated yet. What can I do to remove this flicker?
My store file (user.js) file looks like this:
export const state = () => ({
headers: {},
profile: {}
})
export const mutations = {
updateHeaders (state, headers) {
state.headers.access_token = headers['access-token']
state.headers.token_type = headers['token-type']
state.headers.client = headers['client']
state.headers.expiry = headers['expiry']
state.headers.uid = headers['uid']
if (state.headers.expiry == null) {
state.authenticated = false
} else {
let timeToExpiry = new Date(state.headers.expiry * 1000)
let now = new Date()
state.authenticated = now < timeToExpiry
}
},
signout (state) {
state.headers = {}
state.profile = {}
}
}
The login/logout methods occur via API calls to a Rails app. The Devise gem handles the rest.
Thanks in advance!
EDIT:
I am using Nuxt.js for the layouts/pages/components so I believe that links submit with a this.$router.push(url) under the hood.
The b-nav tags are coming from Bootstrap Vue

When using bootstrap-vue there are two ways to add links to the navbar. One is to bind to :href attribute, which creates a regular html anchor. The other is to use :to attribute, which creates a link that interacts with vue-router.
<b-navbar-nav v-if="isAuthenticated()">
<b-nav-item v-for="nav in authenticated" :key="nav.title" :to="nav.url">{{nav.title}}</b-nav-item>
</b-navbar-nav>
<b-navbar-nav v-if="!isAuthenticated()">
<b-nav-item v-for="nav in unauthenticated" :key="nav.title" :to="nav.url">{{nav.title}}</b-nav-item>
</b-navbar-nav>
No reason to use <template> tags here to encapsulate the . Also note that 'is-nav-bar' is deprecated. See here where they note the deprecation.

What code executes when you click one of the links is not stated, I assume it's something like this.$router.push(url). If this is the case, you've probably have included your navbar in the <router-view>, so when you switch current route, components inside <router-view> rerender, so the navbar flashes. Move them out of the <router-view> should fix this.
edit: so the OP is not using vue-router yet, in this case, either manually change the root component's data to make parts other than the navs change, or add vue-router and use this.$router.push() to navigate so parts outside <router-view> won't change or flash.
Anyway, we need the vue component to stay to let vue to rerender only part of the view, while simply navigating by <a> or something will destruct everything and reconstruct them again, hence the flashing.

Related

Storybook: Clicking a button redirects user

I have a link component that I want to display in Storybook. I'm using Vue.
This is the component
NavigationLink.vue:
<ul>
<li :class="{selected: isSelected}">
<a :href="linkUrl">
<i :class="'isax isax-' + linkIcon"></i>
{{ linkName }}
</a>
</li>
</ul>
This is the story NavigationLink.stories.js:
import NavigationLink from '../app/javascript/components/NavigationLink.vue'
export default {
title: 'Navigation/Links',
component: NavigationLink,
argTypes: { ...argTypes here... }
}
const Template = (args) => ({
components: { NavigationLink },
setup() {
return { args };
},
template: '<NavigationLink v-bind="args"></NavigationLink>',
});
export const UnselectedLink = Template.bind({});
UnselectedLink.args = { ...args here... }
export const SelectedLink = Template.bind({});
SelectedLink.args = { ...args here... }
Unfortunately, you're still able to 'click' the link in Storybook and this then redirects to the 'Introduction' page within Storybook. But I don't want it to redirect there at all. In fact, if possible, I'd prefer to not be able to click it at all.
Any suggestions? Thanks in advance.
In the end I couldn't find a fancy way of doing this, so I just set the :href prop to be linkHref: '/?path=/story/navigation-links--unselected-link' in the args for UnselectedLink (and then similar for the SelectedLink). This still allows for the link to be clicked but will just redirect you back to the same story within Storybook.

Nuxt link with dynamic `to` prop

I am trying to automatically add UTM tracking parameters to all nuxt links, so I made a component for this (let me know if there's a better way!).
<template>
<nuxt-link :to="url" :target="$attrs.target"><slot></slot></nuxt-link>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
name: 'ArticleLink',
props: {
to: {
type: String,
required: true,
},
date: {
type: Number,
required: true,
},
},
computed: {
url(): string {
const url = this.to.startsWith('/')
? `${process.env.VUE_APP_ORIGIN}${this.to}`
: this.to;
const urlObject = new URL(url);
urlObject.searchParams.append('utm_source', 'article');
urlObject.searchParams.append('date', this.date);
console.log(this.to, urlObject.href);
return urlObject.href;
},
},
});
</script>
The problem is when I use it on relative links, the to property for some reason combines the initial href, with the calculated one:
<article-link date="20221109" to="/">link</article-link>
this is rendered as:
http://localhost:3000/current/path/http://localhost:3000/?utm_source=article&date-20221109`
What's strange is the console.log shows the correct conversion from / to http://localhost:3000/?utm_source=article&date=20221109
Also strange is if I use Vue devtools to inspect the <NuxtLink> it shows a correct to prop.
Seems I'm using vue router links incorrectly (see: https://github.com/vuejs/vue-router/issues/1131)
When I pass an absolute URL to the :to prop, it adds it to the end of the current URL. So I have to return urlObject.pathname + urlObject.search instead of returning urlObject.href
:shrug:

Vue 2 make a div Clickable and image

I am playing around with Vue 2, and I want to make a whole div clickable.
This div have a link, image and text.
I used router-link for links in header and other links but when I try to use something else the page keeps refreshing.
Can someone please help me get over this somehow..
Cheers!
Add click event to you <div> that you want to be clickable as below:
<div #click="clickMethod"></div>
Now in your methods property add rhe clickMethod callback that you want to fire when clicked like below
methods: {
clickMethod() {
//add code that you wish to happen on click
}
}:
For anyone who is stuck here like I did on how to make a Div Clickable
<div #click="clickeMethod">
<p> Some Text Here </p>
</div>
script:
<script>
export default {
name: 'headers',
data() {
return {
};
},
methods: {
clickMethod() {
this.$router.push('home');
},
},
};
</script>
This Event will make a div Clickable.
Hope I helped someone :) and thnx to #user7814783
For those wondering how router.push method works, below are various ways you can use the method:
// literal string path
router.push('home')
// object
router.push({ path: 'home' })
// named route
router.push({ name: 'user', params: { userId: '123' } })
// with query, resulting in /register?plan=private
router.push({ path: 'register', query: { plan: 'private' } })
For me this implementation worked best:
<script>
export default {
name: 'home',
data() {
return {
};
},
methods: {
clickMethod() {
this.$router.push({ path: 'home' });
},
},
};
</script>

Navigating vuejs SPA via routes that share component does not refresh component data as expected

I have a couple routes in my vuejs SPA that I have set up using vue-router:
/create/feedback
/edit/feedback/66a0660662674061b84e8ea2fface0e4
The component for each route is the same form with a bit of smarts to change form values based on the absence or present of the ID in the route (feedbackID, in my example).
I notice that when I click from the edit route to the create route, the data in my form does not clear.
Below is the gist of my route file
import FeedbackFormView from './components/FeedbackForm.vue'
// Routes
const routes = [
{
path: '/create/feedback',
component: FeedbackFormView,
name: 'FeedbackCreate',
meta: {
description: 'Create Feedback',
}
},
{
path: '/edit/feedback/:feedbackId',
component: FeedbackFormView,
name: 'FeedbackEdit',
meta: {
description: 'Edit Feedback Form'
},
props: true
}
]
export default routes
Below is the gist of my component
<template lang="html">
<div>
<form>
<input v-model="model.someProperty">
</form>
</div>
</template>
<script>
export default {
data() => ({model: {someProperty:''}}),
props: ['feedbackId'],
created() => {
if (!this.$props['feedbackId']) {
return;
}
// otherwise do ajax call and populate model
// ... details omitted
}
}
</script>
However, if I modify my component as follows, everything works as expected
<template lang="html">
<div>
<form>
<input v-model="model.someProperty">
</form>
</div>
</template>
<script>
export default {
data() => ({model: {someProperty:''}}),
props: ['feedbackId'],
created() => {
if (!this.$props['feedbackId']) {
return;
}
// otherwise do ajax call and populate model
// ... details omitted
},
watch: {
'$route' (to, from) {
if (to.path === '/create/feedback') {
this.model = {}
}
}
}
}
</script>
Why is this? Why do I need watch?
I would have though that changing routes would be sufficient as the purpose of routing is to mimic the semantic behavior of page navigation
You have same component for different routes, when you go to edit route from the create route component is already created and mounted so the state of the component doesn't clear up.
Your component can listen to route changes using $router provided by vue-router every time the route changes the watcher is called.
For those who come this later, the following answer addresses the issue I was facing:
Vue-Router: view returning to login page after page refresh

Hiding Element Based on Route Path or Params in Vue

I'm trying to hide the main app navigation bar based on if the route is on a given path.
In my App.vue component, in the created() method. I do check to see if the route is x || y, if either of those are true, I set my Vuex state of show to false. If it is any other route besides those two, I set show = true.
Then in my template I do this
<template>
<div id="app">
<navigation v-show="show"></navigation>
<router-view></router-view>
</div>
</template>
I'm noticing in Vuex tools that my mutations aren't even registering so I'm not sure why that is. Do they need to be actions instead? Here is my full code.
<template>
<div id="app">
<navigation v-show="show"></navigation>
<router-view></router-view>
</div>
</template>
<script>
import Navigation from './components/Navigation/Navigation'
import { firebaseAuth } from './firebase/constants'
import store from './store/index'
export default {
name: 'app',
components: {
Navigation
},
computed: {
show () {
return store.state.navigation.show
}
},
created() {
// Checks for a user and dispatches an action changing isAuthed state to true.
firebaseAuth.onAuthStateChanged(user => {
console.log(store.state.authentication);
console.log(user);
store.dispatch('checkUser', user);
});
// Check if given route is true, if it is then hide Nav.
if (this.$route.path === "/dashboard/products" || this.$route.path === "/dashboard/settings") {
store.commit('hideNav');
} else if (this.$route.path !== "/dashboard/products" || this.$route.path !== "/dashboard/settings") {
store.commit('showNav');
}
}
};
</script>
This may not be working as created is called only once after the instance is created. but when routes changes, it will not be called, so not triggering the mutations you are expecting to trigger on route change, instead of this, you can put a watch on route, so on each route change, you can check whether to show your Nav Bar or not, like following;
Working fiddle: http://jsfiddle.net/ofr8d85p/
watch: {
$route: function() {
// Check if given route is true, if it is then hide Nav.
if (this.$route.path === "/user/foo/posts") {
store.commit('SHOWNAV');
} else {
store.commit('HIDENAV');
}
}
},