using v-if to use different route-links in header cause route to wrong path - vue.js

I am new to JavaScript Frameworks(although I used vanilla js or jquery(a little)) and I started with VueJS for my project.
I am writing a single page application that uses an restful-api as server.
I wanted that in header some links will appear if user is not logged-in(register, login) and disappear once logged in. And, of course, different set of links will appear.
a bit visualization :)
before login
HEADER => | Register(to='/register') Login(to='login')
after a successful login
HEADER => | Home(to='/') Profile(to='/profile') SomeOtherLink(to='/someOtherPath')
but sometimes, after login, although header is rendered correctly(I look in inspect and links were correct), It goes wrong pathes.
Such as first link before login was register and first link after login is home. When I clik home it goes to register. It is same for Profile and login. when I click profile it goes to /login. But third one works.
It seems somehow renders (not) wrong eventListener for links.
Here is my template.
...
<ul class="navbar-nav">
<template v-if="loggedIn">
<router-link :to="{name: 'Home'}"
tag="li"
class="nav-item"
active-class="active">
<a class="nav-link"> Home </a>
</router-link>
<router-link to="/profile"
tag="li"
class="nav-item"
active-class="active">
<a class="nav-link"> Profile </a>
</router-link>
<router-link :to="{name: 'SomeRoute'}"
tag="li"
class="nav-item"
active-class="active">
<a class="nav-link"> SomeLink </a>
</router-link>
</template>
<template v-else>
<router-link :to="{name: 'Register'}"
tag="li"
class="nav-item"
id="register-link"
active-class="active">
<a class="nav-link"> Kayıt Ol! </a>
</router-link>
<router-link
:to="{name: 'Login'}"
tag="li"
class="nav-item"
id="login-link"
active-class="active">
<a class="nav-link"> Giriş Yap </a>
</router-link>
</template>
</ul>
...
here loggedIn is computed property which is set true if user logs in, and false vice versa. It works correctly.
I tried using div instead of template tag but result was same.
How can I fix this?
Am I in wrong place? Or is it a bug?

I printed loggedIn and it was correct as I mentioned before.
Problem was not that though, Component rendered correctly. Problem was the click event listener I suppose.
Adding unique key to each route-link solved problem.
Most probably vue create an event-listener for each link, and once old links are destroyed and new links are rendered, it creates new listeners for new link but not remove old ones. Since there was no identity in links, it maps according to order.
Once old link is replaced with new one, it has two listener, one for old link that stayed in the same place, one for intended for the link. So it triggers one of them when clicked.
Actually it explains that why it works as intended first, after refresh works wrong, after another refresh again works correctly.
I'm not sure if it is a bug or not, but adding key is the only and true solution as it seems.

Related

Nuxt.js 3 and on-site anchor navigation

I am learning Nuxt.js 3 and am writing a simple project with a one-scroll design that has a menu with anchor links. Clicking a link, the site should automatically scroll to the anchor (like a div with an id).
To test this I set up a simple Nuxt 3 installation with npx nuxi init nuxt-app, removed the demo content and replaced it with this:
pages/index.vue
<template>
<div>
<div id="home"><p>hello world</p></div>
<div class="menu">
<nuxt-link :to="{ path: '/', hash: '#ciao' }">
link
</nuxt-link>
</div>
<div style="height: 3000px">placeholder</div>
<div id="ciao"><p>ciao world</p></div>
<div class="menu">
<nuxt-link :to="{ path: '/', hash: '#home' }">
link
</nuxt-link>
</div>
<div style="height: 3000px">placeholder</div>
</div>
</template>
The problem is, that it is not working. The url in the browser is changed to localhost:3000/#ciao or localhost:3000/#home on click. But the view is not being changed.
Is there something else I need to set up in nuxt, to get anchor navigation to work?
As answered here: https://github.com/nuxt/framework/discussions/5561#discussioncomment-3007717
Using a regular a tag solves the issue
<a href="/#ciao">
go to ciao's hash
</a>
Otherwise, you can also use
<nuxt-link :to="{ hash: '#home' }" :external="true"> <!-- no need for a path if same page -->
go to home's hash
</nuxt-link>
The external prop (Nuxt3 only) is detailed here: https://v3.nuxtjs.org/api/components/nuxt-link#props
Quote from the documentation
Forces the link to be considered as external (true) or internal (false). This is helpful to handle edge-cases.

Router link in inner component

Im using Vue and Ionic, this is my code:
I have a page where I have this cards:
<yt-course-card
v-for="lesson in ytTheme.lessons"
:key="lesson"
:name="lesson.group_name"
:lessons="lesson.group_lessons"
></yt-course-card>
in this card i have this:
<ion-card>
<ion-card-header>
<ion-card-title>{{ name }}</ion-card-title>
</ion-card-header>
<ion-card-content>
<ul>
<li v-for="lesson in lessons" :key="lesson">{{ lesson.name }}</li>
</ul>
</ion-card-content>
</ion-card>
This all works and does what I want it to, but I would like this li to be link to other page. I have this other page ready, but I dont know how should I write my router-link, because if I use router-link in -li- element, it doesnt work.
This is how my route-link should look like, but I dont know where or how to put it, so my li element is link to other page.
:router-link="`/yt-course/${lesson.slug}`"
In vue router router-link is not an attribute but a component. To navigate to other pages use the to attribute like show below:
<li :to="{ mame: 'yourRouteName' }">Link to a page</li>
This works fine and all, but it is the best practice to use Vue router's build-in component, like shown below:
<li>
<router-link :to="{ mame: 'yourRouteName' }">Link to a page</router-link>
</li>

How to add the 'exact' prop in a v-for loop

I am trying to figure out how to add the exact property to the root link in a dynamically loaded menu. The menu entries are loaded from a REST API.
Currently the root path is always matched since it does not contain the exact property. In similar examples the root link is hard coded which I want to avoid.
This is the route that should be matched exactly
{ path: '/', component: HomePage }
The router-link is part of the menu, which is loaded via axios from the REST API and mapped to the state, including the root link "/". How can I access only this one item in the for loop to add the exact property?
<router-link tag="li" class="link" v-for="item in menu" v-bind:key="item.id" :to="item.slug">{{ item.content }}</router-link>
May be I am missing something, or there is a better way to achieve this?
Many thanks
Conditionally bind the exact prop like this.
<router-link tag="li" class="link" v-for="item in menu" :key="item.id" :to="item.slug" :exact="item.slug === '/' ? true : false">
{{ item.content }}
</router-link>
Have you ever try
<router-link tag="li" class="link" v-for="item in menu" v-bind:key="item.id" :to="item.slug" v-bind={ "exact": item.slug === "/" ? true : false }>{{ item.content }}</router-link>

Child component using parent component data to re-direct

I currently have a website/app that displays "FeatureCard" (components) onto a "Grid" component.
The feature cards have all the basic props to pass to the parent (Grid) component, so that data can be grabbed and a v-for used to create as many "featureCards" as possible.
That is all fine and working.
The issue comes when I want a title of a Feature card to link to its on slug-route.
Here is a snippet of my "FeatureCard" component -
<div class="card-content">
<div class="row-1">
<!-- <h4 class="bold">{{ resourceTitle }}</h4> -->
<router-link :to="{ name: 'FigmaFind', params: {resource_slug: this.slug} }"><h4 class="bold">{{ resourceTitle }}</h4></router-link>
<button class="price"><h6 class="bold">{{ resourcePrice }}</h6></button>
</div>
<div class="row-2">
<a :href=creatorProfile target="_blank" >
<img :src="creatorImage">
</a>
<a :href=creatorProfile target="_blank" >
<p>By <strong>{{ creatorsName }}</strong></p>
</a>
</div>
<div class="row-3">
<a :href="downloadLink" class="btn main" target="_blank" >
<p class="light semibold" for="info" >Download Resource</p>
</a>
<a :href="resourceOriginalLink" class="btn icon" target="_blank">
<p class="material-icons light md-18">info</p>
</a>
</div>
</div>
As you can see, in "row-1" I have tried including a router-link, but obviously it doesn't work as it has not yet recieved the data for "this.slug" but essentially, that is where I want a user to click this, and be redirected to "website.com/selected-item-1"
My issue is, I do not pull the "slug" data in, until it is rendered onto the Grid component. So, until then I am not sure how to reference it for later use, I referenced the titles as "resource title" or links as ":href="resourceOriginalLink"" but no idea how to do it for a router item. I could just use ":href="resourceOriginalLink"" but I don't know how I would pass the route-info through...
any ideas?
UPDATE
So, I figured it out. It may be the wrong way to do it, or there may be an easier way. But here is what I have done.
I passed the data "slug" as a prop to my child component (featureCard).
I then set slug: this.resourceSlug in my data.
Then within my title section I simply set <router-link :to="{ name: 'FigmaFind', params: { resource_slug: this.slug }}"><h4 class="bold">{{ resourceTitle }}</h4></router-link>
And, it seems to be working!!
Obviously, if there is a better way please, someone let me know.
UPDATE
So, I figured it out. It may be the wrong way to do it, or there may be an easier way. But here is what I have done.
I passed the data "slug" as a prop to my child component (featureCard). I then set slug: this.resourceSlug in my data. Then within my title section I simply set {{ resourceTitle }}
And, it seems to be working!!
Obviously, if there is a better way please, someone let me know.

vuejs <router-link> component keeps the link to the root path always active

I have my navigation like this
<nav>
<ul class="nav nav-list">
<router-link tag="li" to="/">Home</router-link>
<router-link tag="li" to="/page1">Page1</router-link>
<router-link tag="li" to="/page2">Page2</router-link>
</ul>
</nav>
I want the link to Page1 to be active when ever am in page1 and same for Page2. It is working fine, the links are been activated when I navigate to the pages BUT the problem is that to link to the root (/) page is always active even when I leave the page.
The root link is always active, because Vue Router partially matches the root / path with the current path.
To perform an exact match you can either:
Add an exact attribute to the router-link:
<router-link tag="li" to="/" exact>
<a href="#">
Home
</a>
</router-link>
Set your active class in the linkExactActiveClass router constructor option instead of linkActiveClass.