Passing prop value after switching tabs - vue.js

I have one header page that has two tabs. The two tabs direct to two different pages. I am using the area prop that I defined in the header page router view for the system page. It works fine when I am on the system page, but when I go to the product tab and come back to system tab, the prop in the system tab
loses the value. It shows as the string "area" in the prop. I would like to get the same value when I switch tabs.
<div class="tabs">
<ul>
<li #click="$router.push({ name: 'system' })">
<a>SYSTEM</a>
</li>
<li #click="$router.push({ name: 'product' })">
<a>Product</a>
</li>
</ul>
</div>
<router-view :area="area"/>
</div>
</template>
System view:
export default {
props: ['area'],

Instead of using #click binding you will be much better off using <router-link> especially since you are using the router-view
this will allow you to pass params simply in your app.
https://router.vuejs.org/api/
you declare your routes like so
const routes = [
{
path: '/glossary/:subjectName',
name: 'glossaryWithParams',
component: GlossaryPage,
},
]
Then in your code you can build the element simply like so:
<router-link
:to="`/glossary/${subjectName}`"
class="tabs__link"
>
Subject Glossary
</router-link>
```
Once you implement Router-link your history push state will also be resolved, and your params will work normally.

Related

Vue router-link active-class when some child route duplicates the parent

I have a routing structure such as:
{
path: '/post/'
component: Post,
children: [
{
path: '/',
component: Text,
},
{
path: 'text/',
component: Text,
},
{
path: 'video/',
component: Video,
},
],
}
The important thing is that the route /post/text/ is just an "alias" to the /post/ (root point in this case). Also the router configuration has the custom linkActiveClass option such as 'act' with some defined styles.
In the parent template I have the kind of tabs with router-link's:
<ul>
<li>
<router-link to="/post/text/">Text</router-link>
</li>
<li>
<router-link to="/post/video/">Video</router-link>
</li>
</ul>
So the question is: when going to the /post/ route it would be nice the Text tab to be marked as "active" with that 'act' class because it's just a duplicate of the /post/text/ (or vice versa).
I haven't found any mention of this in the Vue Router docs. How such problems are solved competently?
Thanks in advance!
Solution (TL;DR - currently unsupported, fixed in v4)
The easiest way to achieve this - to use route alias prop, like:
{
path: 'text/',
component: Text,
alias: '/',
},
But, unfortunately, active-class doesn't work properly with aliases yet. It has been fixed in Vue Router v4.
You can do something like this
<ul>
<li :class="[currentPage.includes('/post/text/') ? 'activeClass' : 'no-active-class']">
<router-link to="/post/text/">Text</router-link>
</li>
<li :class="[currentPage.includes('/post/video/') ? 'activeClass' : 'no-active-class']">
<router-link to="/post/video/">Video</router-link>
</li>
</ul>
If your header or browser is a single component, you can include all this functionality in each of your links and it should work correctly.
NOTE: The variable with your current path will depend on the type of project and certain settings in it.
Use the Vuex state.
When every pages go to the tab area are mounted, call a setter for currentPage state.
And you can give an active class according to the state value
<li>
<div :class="{ 'active': currentPage === 'Text'}">
<router-link to="text">Text</router-link>></div>
</li>
<li>
<div :class="{ 'active': currentPage === 'Video'}">
<router-link to="video">Video</router-link>></div>
</li>
using exact-active-class solved a similar problem to yours.
docs: https://router.vuejs.org/api/#exact-active-class

How to update properties of component for dynamic router-link in vuejs

So new to Vue and haven't found anything that specifically addresses my issue.
I have a simple Vue app using VueRouter where I am trying to generate a bracket-style sports tournament that records the outcomes of the different games in the tournament.
I need to make an asynchronous axios call to a server to get info on a specific game. I do not know how to update my component properly to get this info.
App.vue is very simple. The home page is just an overview of the bracket
<template>
<div id="app">
<div id="nav">
<router-link :to="{ name: 'bracket'}">Bracket</router-link>
</div>
<router-view />
</div>
</template>
I have a view component, Bracket.vue, and, for now, all I want this view do is provide links to the different matchups in the tourney.
I have made this work pretty well dynamically as follows:
<template>
<div class="home">
<div id="nav">
<div v-for="round in rounds" :key="round">
<router-link v-for="game in gamesPerRound" :key="matchupKey(round, game)" :to="{ name: 'matchup', params: {round: round, game: game} }">Matchup {{ round }} {{ game }}</router-link>
</div>
</div>
</div>
</template>
When link is clicked, I pull up another view component, Matchup.vue, which I would like to display certain data about the matchup. (MatchupService is an axios API instance)
I pass the round # and the game # as props via router-link. These load fine in the Matchup.vue.
However, when I try to make an asynchronous call to get the matchup data, nothing happens. the matchup property never updates, not on the link click or ever. So I either get nothing (when I use a ternary as per below) or an error if I just try to use the matchup value
Matchup.vue
<template>
<div class="matchup">
<h1>Round {{ round }}</h1>
<h2>Game {{ game }}</h2>
<h6>Team 1: {{matchup.teams ? matchup.teams[0]: 0}}</h6>
<h6>Team 2: {{matchup.teams ? matchup.teams[1] : 0}}</h6>
</div>
</template>
<script>
import MatchupService from '#/services/MatchupService.js'
export default {
props: ["round", "game"],
data() {
return {
matchup: {},
}
},
async updated() {
let matchups = await MatchupService.getMatchups()
this.matchup = matchups.data.rounds[this.round].games[this.game]
},
}
</script>
I have tried many different approaches on this: updated vs. created vs. other hooks, I've tried to update a property post-load and hook that to v-if, etc.
I am just totally stymied on this so hoping for some help.
Looks like you need to use navigation hook beforeEnter in your router.js file or you can use beforeRouteEnter hook directly in your compoennt file. (NOTICE! Using beforeRouteEnter in a component file you can't access 'this', so maybe, there is a reason to use Vuex if you want to store some data within serfing your app). There you can define/fetch/set any data before redirect user to a specific page. By this way you do any actions you want and set it before redirecting.
More about you can find here:
https://router.vuejs.org/guide/advanced/navigation-guards.html

To reopen the same page with different filter

In my Laravel 5/vuejs 2/ VueRouter / app I have navigation area :
<li class="navigation_item_top">
<router-link :to="{ name: 'DashboardIndex' }" class="a_link">
Dashboard
</router-link>
</li>
<li class="active navigation_item_top" >
Customers
<ul class="collapse list-unstyled ml-2" id="backendCustomersSubmenu">
<li class="mt-2">
<router-link :to="{name : 'listCustomers'}" class="a_link">
Customers
</router-link>
</li>
<li class="mt-2">
<router-link :to="{name : 'listCustomers', params: { filter: 'new' } }" class="a_link">
New Customers
</router-link>
</li>
<li class="mt-2">
<router-link :to="{name : 'listCustomers', params: { filter: 'inactive' } }" class="a_link">
Inactive Customers
</router-link>
</li>
</ul>
</li>
Where there are 3 links listCustomers with different default filter opening page and it works ok if from
DashboardIndex page I move to any listCustomers.
But it does not work if from opened listCustomers page I open listCustomers with other filter.
I suppose as VueRouter considers all listCustomers as 1 page.
If there is a way to make it and selecting other listCustomers to reopen filter?
Not sure if i understand your Q very well, but i think you want to:
Append query to url. e.g company.com/listCustomers?filter=new
Make Vue notice the change and do something with that change
If that's the case then try this :
<template>
<--! HTML HERE -->
</template>
<script>
export default {
watch: {
'$route'(to, from){
// you don't need this but just in-case.
//this.$forceUpdate();
//If you're using query like ?filter=new, this should work
if (this.$route.query.filter) {
if (this.$route.query.filter== 'new') {
}else{
}
}
}
}
}
</script>
N.B
If you want use parameters it means you expect your url to be:
domain.com/listCustomers/inactive.
so just try the basics and link like this:
<router-link to="/listCustomers/inactive">
or
<router-link to="/listCustomers/new">.
And if your want queries your url is going to be:
domain.com/listCustomers?filter=new.
and you should pass exact prop to activate active page style.
then you need to watch for changes in the watch hook just like i did my answer
Now all the been said,
linking with parameters should work without any problem, but if you decide to use any Navigation Guards technique, like router.afterEach.
Please do not forget to add next(), to allow it to move on after your code. otherwise it won't navigate.
read Navigation Guards.
I hope you will understand.

Unable to create the correct link with vue router-link

I am using the vue rounter-link tag to navigate to a different page. My problem is that it does not seem to navigate to a different page. I think the problem lies within the "params" of the router-link. I do have a blog_id as 2 but it always shows the current page blog_id that I am at.
Initially I have
<router-link v-bind:to="{name:'datascience-single', params: {blog_id: blog.blog_id}}">
<i class="fa fa-cogs" aria-hidden="true"></i>
</router-link>
but then I started to think that maybe it's not taking the blog.blog_id correctly so I replaced it with a fixed value
<li v-for="blog in blogs" v-bind:key="blog.id">
<h1>{{blog.title}}</h1>
{{blog.content}}
<router-link v-bind:to="{name:'datascience-single', params: {blog_id: 2}}">
<i class="fa fa-cogs" aria-hidden="true"></i>
</router-link>
</li>
However the problem remains the same.
This answer assumes you are already on the same route you are navigating to.
Navigating to the same route does not refresh your components. We are used that whenever we go to a different URL the page should refresh, with multipage websites. However Vue router handles this differently. For the router the route, eg: /blogs/{id}, is not changing whenever id is being updated, we are still on /blogs/{id} but with a different parameter.
In order to detect this change we can simply make a watcher on the route parameter:
watch: {
'$route.params.id': (id) => {
// The route is the same, but our parameter changed.
}
}

How to VueJS router-link active style

My page currently has Navigation.vue component.
I want to make the each navigation hover and active. The hover works but active doesn't.
This is how Navigation.vue file looks like :
<template>
<div>
<nav class="navbar navbar-expand-lg fixed-top row">
<router-link tag="li" class="col" class-active="active" to="/" exact>TIME</router-link>
<router-link tag="li" class="col" class-active="active" to="/CNN" exact>CNN</router-link>
<router-link tag="li" class="col" class-active="active" to="/TechCrunch" exact>TechCrunch</router-link>
<router-link tag="li" class="col" class-active="active" to="/BBCSport" exact>BBC Sport</router-link>
</nav>
</div>
</template>
And the following is the style.
<style>
nav li:hover,
nav li:active {
background-color: indianred;
cursor: pointer;
}
</style>
This is how hover looks like now and expected exactly same on active.
I would appreciate if you give me an advice for styling router-link active works.
Thanks.
The :active pseudo-class is not the same as adding a class to style the element.
The :active CSS pseudo-class represents an element (such as a button)
that is being activated by the user. When using a mouse, "activation"
typically starts when the mouse button is pressed down and ends when
it is released.
What we are looking for is a class, such as .active, which we can use to style the navigation item.
For a clearer example of the difference between :active and .active see the following snippet:
li:active {
background-color: #35495E;
}
li.active {
background-color: #41B883;
}
<ul>
<li>:active (pseudo-class) - Click me!</li>
<li class="active">.active (class)</li>
</ul>
Vue-Router
vue-router automatically applies two active classes, .router-link-active and .router-link-exact-active, to the <router-link> component.
router-link-active
This class is applied automatically to the <router-link> component when its target route is matched.
The way this works is by using an inclusive match behavior. For example, <router-link to="/foo"> will get this class applied as long as the current path starts with /foo/ or is /foo.
So, if we had <router-link to="/foo"> and <router-link to="/foo/bar">, both components would get the router-link-active class when the path is /foo/bar.
router-link-exact-active
This class is applied automatically to the <router-link> component when its target route is an exact match. Take into consideration that both classes, router-link-active and router-link-exact-active, will be applied to the component in this case.
Using the same example, if we had <router-link to="/foo"> and <router-link to="/foo/bar">, the router-link-exact-activeclass would only be applied to <router-link to="/foo/bar"> when the path is /foo/bar.
The exact prop
Lets say we have <router-link to="/">, what will happen is that this component will be active for every route. This may not be something that we want, so we can use the exact prop like so: <router-link to="/" exact>. Now the component will only get the active class applied when it is an exact match at /.
CSS
We can use these classes to style our element, like so:
nav li:hover,
nav li.router-link-active,
nav li.router-link-exact-active {
background-color: indianred;
cursor: pointer;
}
The <router-link> tag was changed using the tag prop, <router-link tag="li" />.
Change default classes globally
If we wish to change the default classes provided by vue-router globally, we can do so by passing some options to the vue-router instance like so:
const router = new VueRouter({
routes,
linkActiveClass: "active",
linkExactActiveClass: "exact-active",
})
Change default classes per component instance (<router-link>)
If instead we want to change the default classes per <router-link> and not globally, we can do so by using the active-class and exact-active-class attributes like so:
<router-link to="/foo" active-class="active">foo</router-link>
<router-link to="/bar" exact-active-class="exact-active">bar</router-link>
v-slot API
Vue Router 3.1.0+ offers low level customization through a scoped slot. This comes handy when we wish to style the wrapper element, like a list element <li>, but still keep the navigation logic in the anchor element <a>.
<router-link
to="/foo"
v-slot="{ href, route, navigate, isActive, isExactActive }"
>
<li
:class="[isActive && 'router-link-active', isExactActive && 'router-link-exact-active']"
>
<a :href="href" #click="navigate">{{ route.fullPath }}</a>
</li>
</router-link>
https://router.vuejs.org/en/api/router-link.html
add attribute active-class="active"
eg:
<ul class="nav navbar-nav">
<router-link tag="li" active-class="active" to="/" exact><a>Home</a></router-link>
<router-link tag="li" active-class="active" to="/about"><a>About</a></router-link>
<router-link tag="li" active-class="active" to="/permission-list"><a>Permisison</a></router-link>
</ul>
When you are creating the router, you can specify the linkExactActiveClass as a property to set the class that will be used for the active router link.
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
const router = new VueRouter({
routes,
linkActiveClass: "active", // active class for non-exact links.
linkExactActiveClass: "active" // active class for *exact* links.
})
This is documented here.
As mentioned above by #Ricky vue-router automatically applies two active classes, .router-link-active and .router-link-exact-active, to the <router-link> component.
So, to change active link css use:
.router-link-exact-active {
//your desired design when link is clicked
font-weight: 700;
}
Just add to #Bert's solution to make it more clear:
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
const router = new VueRouter({
routes,
linkExactActiveClass: "active" // active class for *exact* links.
})
As one can see, this line should be removed:
linkActiveClass: "active", // active class for non-exact links.
this way, ONLY the current link is hi-lighted. This should apply to most of the cases.
David
Let's make things simple, you don't need to read the document about a "custom tag" (as a 16 years web developer, I have enough this kind of tags, such as in struts, webwork, jsp, rails and now it's vuejs)
just press F12, and you will see the source code like:
<div>
page1
page2
page3
</div>
so just add styles for the .router-link-active or .router-link-exact-active
If you want more details, check the router-link api:
https://router.vuejs.org/guide/#router-link