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

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>

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.

Dynamic class regarding the route path

With Vue 2.6, I have a link rendered with router-link, like this:
<router-link class="nav-link" to="/about" v-if="isLogged"><i class="fas fa-sun"></i> About</router-link>
Regarding the value of the route {{ $route.path }}, I would like to add a class to obtain a render like this:
<router-link class="nav-link active" to="/about" v-if="isLogged"><i class="fas fa-sun"></i> About</router-link>
See the word "active" added.
How to do that? I tried a lot of things following the documentation, without success.
Vue does this automatically for you: https://router.vuejs.org/api/#active-class.
However, if you still want to apply your own active class you can bind it using v-bind:
<router-link class="nav-link" :class="{ 'active': $route.path === '/about' }" to="/about" v-if="isLogged"><i class="fas fa-sun"></i> About</router-link>
This way it evaluates the truthiness of the condition on the right of the colon and, if true, applies the class given on the left.
https://v2.vuejs.org/v2/guide/class-and-style.html#Object-Syntax

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

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.

bootstrap vue all b-dropdown-item with router link are active

I'm really enjoying Vue and Bootstrap Vue - very nice. However, one small issue - here's my template:
<template>
<div >
<b-dropdown variant="link" size="lg" no-caret>
<template slot="button-content">
<img src="../assets/logo.png">
</template>
<div v-for="row in navOptions" :key="row.id">
<b-dropdown-item v-bind:to="row.url" >{{ row.text }}</b-dropdown-item>
</div>
<b-dropdown-item>User</b-dropdown-item>
</b-dropdown>
</div>
</template>
The generated html for the items in the v-for loop is:
<a data-v-6949e825="" href="/xxxx/map" class="dropdown-item active" role="menuitem" target="_self">Map</a>
My problem is the "active" that is added to the class, it looks poor:
and isn't relevant as the items are not active.
I have the same issue with <b-nav-item>
Is there bootstrap vue way to disable 'active'?
You can do something like this:
<router-link to="/" tag="li" active-class="active" exact><a>Home</a></router-link>
This is going to create an tag with the active property and is going to change on the bases of the route.
If you are creating nested routes like /something/somethingelse is order to add the active class at /something li you need to add the exact property
For me does not work. The solution that worked for me:
<b-dropdown-item :to="{ path: '/new-task' }">New Task</b-dropdown-item>
More here: https://github.com/bootstrap-vue/bootstrap-vue/issues/3066#issuecomment-491258662

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.