Vue, active router-link not updating after first change - vue.js

I have a strange issue which I struggled with for a long time... I have a Sidebar with several router-links.
<template>
<div class="nav nav-pills main-nav" id="sidebar" role="tablist">
<router-link class="nav-link" to="/tasks">
Tasks
</router-link>
<router-link class="nav-link" to="/notes">
Notes
</router-link>
<router-link class="nav-link" to="/sounds">
Sounds
</router-link>
<div class="account">
<router-link class="nav-link" to="/profile">
Profile
</router-link>
<a class="nav-link" #click="logOut">
Log out
</a>
</div>
</div>
</template>
<script>
export default {
name: "sidebar",
methods: {
...
}
};
</script>
I have a strange issue with the behaviour of the latter menu. If I go to localhost:8080/notes, the notes button is active as expected. Then, if I click on any of the other link(let's say /tasks), it becomes active as expected. However, if I click on some link once again, /tasks remains active and the new page does not become active. That is, I will be able to navigate to other pages, but the /tasks router link will be active whatever I do. In other words, the "active" link is only updated once... I have no idea what the problem might be :/ Any suggestions are greatly appreciated!
In App.vue I import the sidebar
<div id="app">
<router-view name="header"></router-view>
<router-view name="sidebar"></router-view>
<main :class="{ 'remove-width': sidebarOpened }">
<fade-transition origin="center" mode="out-in" :duration="250">
<router-view />
</fade-transition>
</main>
</div>
My router.js start with
let router = new Router({
mode: "history",
linkExactActiveClass: "exact-active",
linkActiveClass: "active",
base: process.env.BASE_URL,

Pretty sure you shouldn't be having multiple router-views in your template. Can you remove those / replace them with the actual components and see if it fixes the issue? Something like this:
<div id="app">
<AppHeader />
<AppSidebar />
<main>
<fade-transition>
<router-view>
</fade-transition>
</main>
</div>
If it doesn't, you should post a bit more code of your components and router to help identify the issue.

Related

Global CSS doesn’t work when programmatically change route

My index page has a Header and Home components.
{
path: "/",
name: "Homepage",
components: {
header: () => import("../components/layout/HomepageHeader.vue"),
main: () => import("../views/Home.vue"),
},
},
Both don’t have any scoped CSS, they only use Tailwind CSS classes. And that Tailwind CSS file is global.
The problem occurs when I programmatically redirect to my index page (for example, after I logged in). The HomepageHeader component doesn’t have all the CSS. Same with the Home.vue component.
Here’s the simplified code of the Header
<template>
<div class="w-full bg-white sm:shadow-lg mb-6">
<div class="mx-6">
<nav
class="px-3 flex flex-col sm:flex-row sm:px-4 sm:justify-between sm:h-16 list-none"
>
<li
class="text-center h-16 font-semibold text-red-500 text-lg sm:text-xl sm:h-16 table"
>
<div class=" py-4 sm:h-16">
<router-link to="/">Home</router-link>
</div>
</li>
<li>
<login-badge
class="shadow-lg md:shadow-none px-4 py-3 flex sm:px-0 sm:h-14 text-white"
></login-badge>
</li>
</nav>
</div>
</div>
</template>
When I say
“component doesn’t have all the CSS”
I mean that all the classes in the HomepageHeader.vue component don’t work. But the classes inside the child component LoginBadge.vue work fine!
What else can I say? I use router.replace("/"); to redirect to the homepage.
How do I fix that?
P.S. Just found out that the same thing happends when I go back to a previous page and that previous page is the index page.
P.P.S. global CSS is imported inside the App.vue
<template>
<router-view name="header"></router-view>
<main class="container mx-auto">
<router-view name="main"></router-view>
</main>
</template>
<script>
export default {
setup() {
return {};
},
};
</script>
<style>
#import "./assets/tailwind.css";

Set up router for subpages in Vue.js

I wounder how I best set up the router in Vue.js for handling ”subpages”. For example I got a navbar that routes to different pages. From one of these pages I want to have links to subpages. How do I best set this up?
I have done like this so far:
App.js
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<router-view />
</div>
</template>
Then I set up my router:
export default new Router({
routes: [
{
path: "/",
name: "home",
component: Home
},
{
path: "/about",
name: "about",
component: About,
children: [
{
path: "/child1",
name: "child1",
component: Child1
}
]
}
]
})
And my About.vue where I provide the link to Child1
<template>
<div class="about">
<h1>This is an about page</h1>
<router-link to="/child1">Child1</router-link>
<router-view></router-view>
</div>
</template>
And finally my Child1.vue
<template>
<div class="child1">
<p>My message</p>
</div>
</template>
My problem is that the link to Child1 is displayed both on the About page and on Child1 page. I just want to display it on the about page and only the content from the Child1 on the Child1 page
How is the best practice of setting up things like this?
Thanks
My problem is that the link to Child1 is displayed both on the About page and on Child1 page. I just want to display it on the about page
Just to clarify what's happening here: the link to Child1 is always visible within the About component even if child routes are active, but you don't want to show the link when the child route is active.
Way 1
You can provide fallback content to <router-view> when there is no matching route (i.e. when no child route is active). This would be a good opportunity to show the link.
<template>
<div class="about">
<h1>This is an about page</h1>
<router-view>
<router-link to="/child1">Child1</router-link>
</router-view>
</div>
</template>
Way 2
The above solution may not work if your template is more complicated and if you want to situate the link elsewhere in the template.
So you'll have to manually control the visibility of the link by using v-if so that it is only visible when the child route is not active.
<template>
<div class="about">
<h1>This is an about page</h1>
<!-- Show only when no child routes are active -->
<router-link v-if="$route.name === 'about'" to="/child1">Child1</router-link>
<!-- Or, do not show when Child1 route is active -->
<router-link v-if="$route.name !== 'child1'" to="/child1">Child1</router-link>
<router-view></router-view>
</div>
</template>

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

Vue on click inside component

I have component like this:
<template>
<!-- Content for section1 -->
<div id="section1" class="container-fluid home-div">
<h1 class="text-center">Create Your own Website!</h1>
<p>Welcome to website builder a place where magic comes true. This website will allow you to create your own website, host it on our webserver and change the content or add content as you go!
</p>
<button v-on:click="next" class="btn btn-primary"></button>
</div>
</template>
and my js:
import Vue from 'vue';
import VueRouter from 'vue-router';
import Section1 from '../vue/components/homepage.vue';
import Section2 from '../vue/components/section2.vue';
const routes = [
{ path: '/', component: Section1 },
{ path: '/about', components: Section2, },
];
Vue.use(VueRouter);
const router = new VueRouter({
routes,
});
new Vue({
el: '#homepage',
router,
});
html:
<div id="homepage">
<nav class="navbar navbar-default navbar-fixed-top">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#myNavbar">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{{ url('template') }}">Website Builder</a>
</div>
<div>
<div class="collapse navbar-collapse" id="myNavbar">
<ul class="nav navbar-nav">
<li><router-link to="/">Create</router-link></li>
<li><router-link to="about">How</router-link></li>
</ul>
</div>
</div>
</nav>
<router-view></router-view>
</div>
So now when I click on the link nothing happens, it has a path of /#/ and /#/about respectively, and are being transformed to tags, what is wrong with my current code? Should the navigation be as a template maybe or within the template?
Your component has local scope, meaning it will try and call a next method on the component itself and not on your root instance where you have actually defined your method.
Depending on your situation and what you want you need to either move the method inside your homepage or section2 component.
Or you need to notify your root Vue instance whenever the click event has been called in a child component. There are many communication patterns possible with Vue.js for child-parent communication an easy one would be this:
https://v2.vuejs.org/v2/guide/components.html#Custom-Events
On click you would emit an event from your child component and use a v-on to listen to the event in your root instance.
But by the looks of it you want to implementing routing with one component per page:
https://v2.vuejs.org/v2/guide/routing.html
in your 'new App', when you set 'el' property it should refer to an ID of an html element (the root element).
your html should be like this instead:
<div id="homepage">
<homepage v-if="seen"></homepage>
<section2 v-else></section2>
</div>

unable to get rows of "router.navigation"

While I am new to Aurelia, I am really liking Aurelia. I have read the "Getting started" with Aurelia and have done some reading on configure router. Specifically I am using http://mobilemancer.com/2016/11/18/using-router-in-aurelia-spa/ as a basis for my understanding.
I am following the normal conventions (app.ts, app.html and navigation.html). I am unable to get the "repeat.for" to iterate over the rows (specifically in this case one row) in router.navigation. I am sure that I am making some elementary mistake. But unable to find out what. Both of app.html and navigation.html seem to be "executing" in terms of seeing both "Aurelia Router Demo" and "nice" appearing on the browser.
Thanks for your help!
app.ts
import { autoinject } from 'aurelia-framework';
import { RouterConfiguration, Router } from 'aurelia-router';
#autoinject
export class App {
public router: Router;
configureRouter(config: RouterConfiguration, router: Router): void {
this.router = router;
config.title = ' Test ';
config.map( [
{route: ['', 'home'], name: 'home', moduleId: './home', nav: true, title: 'Home'},
]);
}
}
app.html
<template>
<require from="./navigation.html"> </require>
<h1> Aurelia Router Demo </h1>
<navigation router.bind="router"> </navigation>
<div class="page-host">
<router-view>
</router-view>
</div>
</template>
--navigation.html
<template bindable="router">
<h2> nice </h2>
<nav>
<ul>
<li repeat.for="row of router.navigation">
<a href.bind="row.href"> ${row.title} </a>
</li>
</ul>
</nav>
</template>
I don't see anything wrong at first glance. Try using compose instead of binding the router to see if that works. compose without a specified view-model attribute uses the consumer's view-model, removing the necessity of passing the router through data binding. Note that when requiring a custom element with a .html extension, Aurelia automatically generates a view model for it.
Basically, use compose instead of navigation:
app.html
<template>
<h1> Aurelia Router Demo </h1>
<compose view="./navigation.html"> </compose>
<div class="page-host">
<router-view>
</router-view>
</div>
</template>
And remove bindable="router" from:
navigation.html
<template>
<h2> nice </h2>
<nav>
<ul>
<li repeat.for="row of router.navigation">
<a href.bind="row.href"> ${row.title} </a>
</li>
</ul>
</nav>
</template>
In my configuration , including aurelia-routing.min.js as a script tag does NOT do the work. (Possible duplicate issue at How to install/setup Aurelia router/routes).
If I do a build ( au build) and include the vendor.js, then the thing (repeat.for) works.
Thanks for quick responses!