Vue Router Linking Children from Children - javascript

I am currently trying to get this thing working with Vue Router.
The goal is:
If Nav#1 is clicked, a Component which includes another Router-Link with SubNav#1 preselected should appear.
I could get it to work in the way that if you click Nav#1, the Component with SubNav#1 with an already active class appeared. The problem is that the active class from Nav#1 is removed and it is not possible to navigate to Nav#2 or N#3. If I click on the Router-Link from Nav#2 or #3 nothing happens...
routes.js:
{ path: '/app', component: App,
children: [
{ path: 'Nav#1', component: Nav#1_Content,
children: [
{ path: 'SubNav#1', component: SubNav#1_Content},
{ path: 'SubNav#2', component: SubNav#2_Content}
]},
{ path: 'Nav#2', component: Nav#2_Content},
{ path: 'Nav#3', component: Nav#3_Content},
]}
+ custom linkActiveClass in new VueRouter instance.
Nav component:
<router-link to="Nav#1/SubNav#1">Nav #1</router-link>
<router-link to="Nav#2">Nav #2</router-link>
<router-link to="Nav#3">Nav #3</router-link>
Nav#1_Content component:
<template>
<div>
<nav>
<router-link to="SubNav#1" tag="div">SubNav#1</router-link>
<router-link to="SubNav#2" tag="div">SubNav#2</router-link>
</nav>
<router-view></router-view>
</div>
</template>

When you specify a value for the to prop on a <router-link> tag, you are specifying the exact path of the route to go to.
Since all of your example paths are under the /app root path, I'm not sure how you are getting any of these links to work.
That said, you need to get rid of the # signs in your route path definitions. When providing a path to Vue Router, it strips anything after the # sign and saves it as the $route.hash value.
So, use the full pathname in the to prop for your <router-link> tags and drop the # signs.
Here's a working fiddle.

Related

Save the open state of tabs in vue js

I am creating a project. I've to work with many routes and tabs inside it. My problem is that I want my browser to remember that which tab was opened before refreshing the page.Suppose I have 3 tabs on 1 page and I opened the 2nd or 3rd tab, than I refresh the page it kick me back to 1st tab. How to prevent this.Is their any solution ?
Use child routes. Each tab would content a <router-view> element, which corresponds to a page within a page.
See vue-router docs on Nested Routes
const routes = [
{
path: '/my-page',
component: MainPage,
children: [
{
path: 'tab1',
component: Tab1Content,
},
{
path: 'tab2',
component: Tab2Content,
},
],
},
]
<!-- On /my-page/tab-2, Tab2Content will be rendered in the <router-view> -->
<template>
<v-tab>
<router-view /> <!-- Sub route -->
</v-tab>
</template>

Child route not showing in Vue-router

I've following object in Vue router.
{
path: "/books",
name: "books",
component: Books,
children: [
{
path: ":id",
name: "books.detail",
component: BookDetail,
}
]
}
Within Books.vue component, there're <router-link> that upon clicking each; it should go to its detail view.
<router-link :to="{ name: 'book.detail', params: { id: 5 }}">
<h2>Book Name</h2>
</router-link>
Similarly in App.vue I've following <router-view>.
<div class="books">
<router-view></router-view>
</div>
When I click <router-link> the path gets updated but the respective BookDetail Page doesn't show up.
It's first time I am integrating routers within a project and couldn't figured out how to solve it, even going in depth reading the documentation.
In Books.vue there needs to be an additional <router-view> to show the nested route component. Without it, you would get the behavior you describe.
Books.vue
<template>
<div>
Books
<router-view></router-view>
</div>
</template>
Or if you didn't want nested routing, define the detail page as a separate route:
{
path: "/books",
name: "books",
component: Books
},
{
path: "/books/:id",
name: "books.detail",
component: BookDetail
}
(Note: There's also a typo in the <router-link> (book instead of books) but since you were able to see the correct route path, maybe that's just a typo in your post.)

Vue.js - switching from one component to another

I'm quite new to vue.js.
I have a page that lists details of a list of people, with an 'Edit' button against each person.
When I click the Edit button, I want to switch to another page which shows a form for editing the selected person's details.
I'm not sure what is the best way to do this. I'm using Bootstrap and Router in my solution.
Option 1: I thought it would be straightforward to route to '/person/:id' when clicking the Edit button, but not sure how to do this from the click handler method.
Option 2:
Below is the main component, where I'm trying to switch between the two components 'PersonsList' and 'EditPersonData' upon receiving an event from either. PersonsList is emitting the event successfully, but I'm not sure how to listen to it here and switch to EditPesonData component.
Home.vue
<template>
<div class="home">
<h1 class='home-text'>
{{message}}
</h1>
<component v-bind:is="component"/>
</div>
</template>
<script>
import PersonsList from '#/components/PersonsList'
import EditPersonData from '#/components/EditPersonData'
export default {
name: 'Home',
data() {
return {
message: 'Welcome to Person Details!',
component: "PersonsList"
}
},
components: {
PersonsList,
EditPersonData
}
};
</script>
Any help would be very much appreciated.
Thanks
The best way would be to send the id as a param in the route /person/:id.
To do this, you can append the id of each list item, for example:
<div v-for="(user,index) in userList" :key="index">
<a :href="'/person/' + user.id">Edit</a>
</div>
Then, you would get it in the other component as this.$route.params.id in order to get more details about the user of this id.
Make sure you define the route in the router with the id param as follows: person/:id
UPDATE :
If you want to change the route on a click function:
<b-btn #click="editPerson(user.id)">Edit</b-btn>
And in the methods, add the function as follows:
editPerson(userId){
this.$router.push({
name: 'person',
params: { id: userId }
});
}

Back button creating multiple components on the same page

In my Angular 6 app, there are certain pages that do not respond
appropriately when I click "back". The site will instead spawn
multiple components, instead of redirecting the user back to the
single component they just went to.
For the faulty components, I made the component.html page be one
single line like such as:
// home-page.component.html
home
and
// admin-page.component.html
admin
And then the component.ts page is using default code as well.
On my app.component.html, I just have the following:
<router-outlet></router-outlet>
Now when I go on the home page (via <a
routerLink="/admin">Admin></a>), I correctly see this (more or
less)in my HTML when I inspect the site. And note this is just the RESULTING HTML that appears when I right click - view page source etc... I know my routing is setup correctly as this whole thing works in Google Chrome, but not in Firefox.
<html>
<head>...</head>
<body>
<app-root>
<router-outlet>
<app-admin-page>admin</app-admin-page>
</router-outlet>
</app-root>
</body> </html>
But when I now press "back", I see the below
<html>
<head>...</head>
<body>
<app-root>
<router-outlet>
<app-home-page>home</app-home-page>
<app-admin-page>admin</app-admin-page>
</router-outlet>
</app-root>
</body> </html>
When I pressed "back", it should of DELETED the
<app-admin-page>admin</app-admin-page> and just kept the new
<app-home-page>home</app-home-page>, but it keeps both. I can then
press "forward" and then it'll have 3 components. Any ideas what is
going on here? Note that if I'm on the 'admin' page and click the 'home' link (which does a routerLink thing), it works correctly. It's just the back button messing up.
You are mixing child components and routing. For any particular component, you should use one or the other.
There should be no components defined between the <router-outlet></router-outlet> tags.
Notice in your code above:
<router-outlet>
<app-admin-page>admin</app-admin-page>
</router-outlet>
So either display both components as child components like this:
<app-root>
<app-home-page>home</app-home-page>
<app-admin-page>admin</app-admin-page>
</app-root>
This will show both components, one above the other.
OR
Use routing:
<app-root>
<router-outlet></router-outlet>
</app-root>
This will show one component at a time in this location. Use a route configuration to define which components to display in the router outlet.
RouterModule.forRoot([
{ path: 'home', component: HomeComponent },
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'admin', component: AdminComponent }
]),
I had the same issue, where under certain circumstances when I hit the "Back" button in the browser, it would combine the current component with the one defined for the updated URL hash. I was able to resolve it by adding this in my constructor of each component:
constructor(private ngZone: NgZone, private router: Router, private location: PlatformLocation, private network: NetworkService) {
location.onPopState(() => {
this.ngZone.run(() => this.router.navigateByUrl(location.hash.replace('#/', ''))).then();
});
}
It then properly navigated to a preceding component without combining both on the page.

How to make router-link work with custom link component?

<base-link> component
I have a Vue component <base-link>, which I use every time I want to have an achor. It's mostly for applying styles specific to links, so that all the links across the whole page look the same without applying global styles.
Make <router-link> use <base-link>
When using <router-link> component to create a link, I cannot apply those styles (<base-link> styles are scoped) unless <router-link> uses my <base-link> component to create the anchor element.
Fortunately <router-link> provides tag attribute, which seems to do exactly that. Unfortunately I can't get it to work. I have 2 problems:
All my components are locally registered (I use ES6 modules with Webpack and import components locally every time I need them). <router-link> doesn't know what <base-link> component is and can't render it. Is there a way to inject a local component for <router-link> to use?
To solve problem #1, I thought it's enough to declare <base-link> component globally. Unfortunately it still doesn't work. This time <base-link> component gets rendered properly, but is still not functional - doesn't react to click events. It seems to me the problem is that it's href attribute isn't set at all. Is there a way to make <router-link> set it properly? (without setting it manually)
Question
How do I solve problems #1 and #2? I suspect #1 might be not possible, but I hope at least #2 is.
Code example
Here is a pen with code below, which illustrates both problems.
const router = new VueRouter({
routes: [
{
path: "/",
component: {
template: '<p>Homepage template</p>'
}
},
{
path: "/subpage",
component: {
template: '<p>Subpage template</p>'
}
}
]
});
// Globally registered BaseLink.
Vue.component('BaseLinkGlobal', {
props: {
href: String
},
template: `
<a
:href="href"
class="BaseLinkGlobal"
>
<slot />
</a>
`
})
const vue = new Vue({
el: "#app",
router,
components: {
// Locally registered BaseLink.
BaseLinkLocal: {
props: {
href: String
},
template: `
<a
:href="href"
class="BaseLinkLocal"
>
<slot />
</a>
`
}
},
template: `
<div>
<!-- 2 router links. One uses locally registered BaseLink
-- and the other one a globally registered one. -->
<nav>
<router-link
to="/"
tag="base-link-local"
>
Home
</router-link>
<router-link
to="/subpage"
tag="base-link-global"
>
Subpage
</router-link>
</nav>
<router-view />
</div>
`
});
You can create a base link component which can double as a normal a tag or <router-link> when you wish.
//Base link
<template>
<component :is="type" :class="{'base': type === 'a'}" v-bind="$attrs">
<slot></slot>
</component>
</template>
<script>
export default {
props: {
routerLink: {
type: Boolean,
default: false
}
},
computed: {
type() {
return this.routerLink ? 'router-link' : 'a'
}
}
}
</script>
<style scopex>
.base {
border: 1px solid red;
}
</style>
Usage
when you want to use it as a normal link just do not provide the router-link prop as below:
<base-link>This is a base a tag</base-link>
To use it as a router-link just add the router-link prop along with the to prop:
<base-link router-link to="/">This is router-link</base-link>
Explanation about the base-link component:
We use a component which is provided by vuejs to render a tag or router-link base on the truthiness of the routerLink prop.
A class of .base is added if it is a normal link i.e a
we bind $attrs which allows us to make the component more transparent i.e allows us to use attributes like href or to without passing them as props.
<base-link href="https://google.com">go to google</base-link>
You can have a look here for more explanation about usage of $attrs
This is for solving problem #2
The global component doesn't inherit the event listener of the router link. You can make it inherit by adding v-on="$listeners" to the global component.
// Globally registered BaseLink.
Vue.component('BaseLinkGlobal', {
props: {
href: String
},
template: `
<a
:href="href"
class="BaseLinkGlobal"
v-on="$listeners"
>
<slot />
</a>
`
})
The link works after adding it: https://codepen.io/jacobgoh101/pen/YvqJxL?editors=0010

Categories