Save the open state of tabs in vue js - javascript

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>

Related

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.)

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

Vue Router Linking Children from Children

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.

Meteor- Use a Blaze template in a React template from a route

I am currently developing a reactive web app through meteor that is utilizing the templates:tabs package, which is designed to create a tabular interface. I plan on displaying a data table within these tabs and sending queries to different databases depending on which tab is selected similar to cars.com.
The app already has a FlowRouter which links to two different routes, and I only want the tabs to be present for one of them. The router I wish to have displaying the tabs is as follows.
#router.jsx
FlowRouter.route('/', {
action() {
mount(MainLayout, {
content: (<Landing />)
}
)
}
});
I need to create the following template:
template name="myTabbedInterface">
#Tabs.html
{{#basicTabs tabs=tabs}}
<div>
<p>Here's some content for the <strong>first</strong> tab.</p>
</div>
<div>
<p>Here's some content for the <strong>second</strong> tab.</p>
</div>
<div>
<p>Here's some content for the <strong>third</strong> tab.</p>
</div>
{{/basicTabs}}
</template>
Here is the JS file that has the helpers for the template.
#myTabbedInterface.js
ReactiveTabs.createInterface({
template: 'basicTabs',
onChange: function (slug, template) {
// This callback runs every time a tab changes.
// The `template` instance is unique per {{#basicTabs}} block.
console.log('[tabs] Tab has changed! Current tab:', slug);
console.log('[tabs] Template instance calling onChange:', template);
}
});
Template.myTabbedInterface.helpers({
tabs: function () {
// Every tab object MUST have a name and a slug!
return [
{ name: 'First', slug: 'reports' },
{ name: 'Second', slug: 'sources' },
{ name: 'Third', slug: 'feedback' }
];
},
activeTab: function () {
// Use this optional helper to reactively set the active tab.
// All you have to do is return the slug of the tab.
// You can set this using an Iron Router param if you want--
// or a Session variable, or any reactive value from anywhere.
// If you don't provide an active tab, the first one is selected by default.
// See the `advanced use` section below to learn about dynamic tabs.
return Session.get('activeTab'); // Returns "first", "second", or "third".
}
});
Lastly, here is the file that "Landing" routes to from the router where I want the template to be called:
#Landing.jsx
`import {Blaze} from 'meteor/blaze';
import React, {Component} from 'react';
import { Meteor } from 'meteor/meteor';
export default class Landing extends Component{
render(){
return(
<div>
//Want to render template here
</div>
)
}
}`
So how is it possible to render the (Blaze) template in HTML in a React render? Thank you.
I suggest not using a Blaze package to solve a React problem. I'm aware this is not what you asked, but maybe it helps.
Implementing a tabbed UI is really a simple task, mixing React and Blaze isn't worth it. A nice library to solve this problem is React-Bootstrap, it implements several useful React components like <Tab>:
<Tabs>
<Tab title="Apples">Apple content</Tab>
<Tab title="Pears">Pear content</Tab>
<Tab title="Melons">Melon content</Tab>
</Tabs>
But if you wish to walk that road, you could try blazetoreact.

Categories