Home > Mobile >  VueJS navbar active card indicator doesn't point on first load of component
VueJS navbar active card indicator doesn't point on first load of component

Time:12-22

I have the problem with my navigation bar indicator(blue underline on the screenshot below) navigation bar

Indicator works fine, but when you open page first time(or reload) indicator doesn't appear and there is an error in console: Active link not found.. I think, the reason of this behaviour is that the router is not fully loaded when the mounted event is called.

My Navbar component's code:

<template>
  <nav>
    <RouterLink to="/"><img :src="logo" alt="Logo" /></RouterLink>
    <div >
      <ul  @mouseleave="fixIndicator">
        <li v-for="({ route, name, routeName }, index) in navLinks" @mouseenter="moveIndicator" :route-name="routeName" ref="navigationLinks">
          <RouterLink :to="route">{{ name }}</RouterLink>
        </li>
      </ul>
      <span id="indicator"></span>
    </div>
  </nav>
</template>
<script>
import { animate } from "motion";
import { RouterLink } from "vue-router";
import logo from "../assets/img/logo.png";

export default {
  name: "Navbar",
  data() {
    return {
      logo: logo,
      navLinks: [
        {
          route: "/",
          routeName: "home",
          name: "Home",
        },
        {
          route: "/about",
          routeName: "about",
          name: "About",
        },
        {
          route: "/team",
          routeName: "team",
          name: "Team",
        },
        {
          route: "/demo",
          routeName: "demo",
          name: "Demo",
        },
      ],
    };
  },
  methods: {
    moveIndicator(e) {
      console.log(this.activeLink());
      const node = e.target;
      const nodeLeft = node.offsetLeft;
      const nodeWidth = node.offsetWidth;
      animate("#indicator", { width: `${nodeWidth}px`, left: `${nodeLeft}px` }, { duration: 0.2, easing: "ease-in-out" });
    },
    fixIndicator() {
      const node = this.activeLink();
      if (node == null) {
        console.error("Active link not found.");
        return;
      }
      const nodeLeft = node.offsetLeft;
      const nodeWidth = node.offsetWidth;
      animate("#indicator", { width: `${nodeWidth}px`, left: `${nodeLeft}px` }, { duration: 0.2, easing: "ease-in-out" });
    },
    activeLink() {
      return this.$refs.navigationLinks.find((link) => {
        return link.getAttribute("route-name") == this.$route.name;
      });
    },
  },
  mounted() {
    this.fixIndicator();
  },
};
</script>

I think that some kind of event after full router load could solve the problem, but I don't know of one and also haven't found either.

Library I used for animation: https://motion.dev

CodePudding user response:

It looks like the issue is occurring because the mounted hook is called before the router has finished loading and setting the active route. As a result, when the activeLink method is called in the mounted hook, the this.$route.name value is not yet set and the activeLink method returns null.

One solution to this issue would be to move the call to this.fixIndicator from the mounted hook to the beforeRouteEnter hook in the route component that is being rendered. The beforeRouteEnter hook is called before a route is entered, after the route's components have been resolved, so it would be a good place to update the active link indicator.

CodePudding user response:

One solution to this issue would be to move the call to this.fixIndicator from the mounted hook to the beforeRouteEnter hook in the route component that is being rendered. The beforeRouteEnter hook is called before a route is entered, after the route's components have been resolved, so it would be a good place to update the active link indicator.

I have done something like this and it is not working, is it right way to call that event? There is no console.log, so the function doesn't even execute.

export default {
  name: "Navbar",
  beforeRouteEnter(to, from, next) {
    console.log("test");
    this.fixIndicator();
  },
  data() {
    return {
      logo: logo,
      navLinks: [
        {
          route: "/",
          routeName: "home",
          name: "Home",
        },
        {
          route: "/about",
          routeName: "about",
          name: "About",
        },
        {
          route: "/team",
          routeName: "team",
          name: "Team",
        },
        {
          route: "/demo",
          routeName: "demo",
          name: "Demo",
        },
      ],
    };
  },
  methods: {
    moveIndicator(e) {
      const node = e.target;
      const nodeLeft = node.offsetLeft;
      const nodeWidth = node.offsetWidth;
      animate("#indicator", { width: `${nodeWidth}px`, left: `${nodeLeft}px` }, { duration: 0.2, easing: "ease-in-out" });
    },
    fixIndicator() {
      const node = this.activeLink();
      if (node == null) {
        console.error("Active link not found.");
        return;
      }
      const nodeLeft = node.offsetLeft;
      const nodeWidth = node.offsetWidth;
      animate("#indicator", { width: `${nodeWidth}px`, left: `${nodeLeft}px` }, { duration: 0.2, easing: "ease-in-out" });
    },
    activeLink() {
      return this.$refs.navigationLinks.find((link) => {
        return link.getAttribute("route-name") == this.$route.name;
      });
    },
  },
};
  • Related