Categories
Vue 3

Vue Router 4–Navigation Guards

Vue Router 4 is in beta and it’s subject to change.

To build a single page app easily, we got to add routing so that URLs will be mapped to components that are rendered.

In this article, we’ll look at how to use Vue Router 4 with Vue 3.

Navigation Guards

We can add navigation guards to guard navigation by redirecting or canceling it.

The guards are only triggered when we navigate to different routes.

URL parameters and query changes won’t trigger enter or leave navigation guards.

Global Before Guards

We can add a global before guard by passing a callback to the router.beforeEach method.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vue-router@4.0.0-beta.7/dist/vue-router.global.js"></script>
    <title>App</title>
  </head>
  <body>
    <div id="app">
      <p>
        <router-link to="/foo">foo</router-link>
        <router-link to="/bar">bar</router-link>
      </p>
      <router-view></router-view>
    </div>
    <script>
      const Foo = {
        template: "<div>foo</div>"
      };
      const Bar = {
        template: "<div>bar</div>",
        props: ["id"]
      };
      const routes = [
        {
          path: "/foo",
          component: Foo
        },
        {
          path: "/bar",
          component: Bar
        }
      ];
      const router = VueRouter.createRouter({
        history: VueRouter.createWebHistory(),
        routes
      });

      router.beforeEach((to, from, next) => {
        console.log(to, from);
        next();
      });
      const app = Vue.createApp({});
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>

We call the router.beforeEach method with a callback.

The callback takes a to , from and next parameters.

to is a route object being navigated to.

from is a route object being navigated away from.

A route object has the fullPath , metadata, query parameters, URL parameters, and more.

next is a function that must be called to resolve the hook.

We need to call next to show the to route.

The next function can take an argument.

If we pass in false , then the current navigation is aborted.

If the browser URL is changed, then it’ll reset to the one in the from route.

We can also pass in a path string or an object with the path property with the path string.

Either way, we redirect to a different location.

The current navigation is aborted and a new one will be started.

The object can have extra options like the replace and name properties from the route.

Any properties that are accepted by router.push is accepted.

We can also pass in an Error instance if there are any errors.

Then the navigation will be aborted and the error will be passed to callbacks registered with router.onError().

We should make sure that next is called exactly once in the callback.

Otherwise, the navigation may never resolve or errors may result.

So instead of writing:

router.beforeEach((to, from, next) => {
  if (to.name !== 'login' && !isAuthenticated) {
    next({ name: 'login' })
  }
  next()
})

We write:

router.beforeEach((to, from, next) => {
  if (to.name !== 'login' && !isAuthenticated) {
    next({ name: 'login' });
  }
  else {
    next();
  }
})

Global Resolve Guards

We can register a global guard with router.beforteResolve .

It’s similar to router.beforeEach except that it’ll be called right before the navigation is confirmed.

Conclusion

We can add navigation guards to control navigation with Vue Router 4.

Categories
Vue 3

Vue Router 4–Navigation Guard Hooks

Vue Router 4 is in beta and it’s subject to change.

To build a single page app easily, we got to add routing so that URLs will be mapped to components that are rendered.

In this article, we’ll look at how to use Vue Router 4 with Vue 3.

Global After Hooks

We can create global after navigation hooks.

To do that, we call the router.afterEach method with a callback.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vue-router@4.0.0-beta.7/dist/vue-router.global.js"></script>
    <title>App</title>
  </head>
  <body>
    <div id="app">
      <p>
        <router-link to="/foo">foo</router-link>
        <router-link to="/bar">bar</router-link>
      </p>
      <router-view></router-view>
    </div>
    <script>
      const Foo = {
        template: "<div>foo</div>"
      };
      const Bar = {
        template: "<div>bar</div>"
      };
      const routes = [
        {
          path: "/foo",
          component: Foo
        },
        {
          path: "/bar",
          component: Bar
        }
      ];
      const router = VueRouter.createRouter({
        history: VueRouter.createWebHistory(),
        routes
      });

      router.afterEach((to, from) => {
        console.log(to, from);
      });
      const app = Vue.createApp({});
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>

to add our after navigation hook.

It’s called after we navigated to the route.

to is a route object being navigated to.

from is a route object being navigated away from.

A route object has the fullPath , metadata, query parameters, URL parameters, and more.

There’s no next function to let us do redirects or throw errors.

Per-Route Guard

We can add navigation guards per route.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vue-router@4.0.0-beta.7/dist/vue-router.global.js"></script>
    <title>App</title>
  </head>
  <body>
    <div id="app">
      <p>
        <router-link to="/foo">foo</router-link>
        <router-link to="/bar">bar</router-link>
      </p>
      <router-view></router-view>
    </div>
    <script>
      const Foo = {
        template: "<div>foo</div>"
      };
      const Bar = {
        template: "<div>bar</div>"
      };
      const routes = [
        {
          path: "/foo",
          component: Foo
        },
        {
          path: "/bar",
          component: Bar,
          beforeEnter: (to, from, next) => {
            console.log(to, from);
            next();
          }
        }
      ];
      const router = VueRouter.createRouter({
        history: VueRouter.createWebHistory(),
        routes
      });

      const app = Vue.createApp({});
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>

We have the beforeEnter method in the /bar route object.

The next function lets us resolve the navigation since it’s run before we enter the route.

In-Component Guards

We can add navigation guards inside a component.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vue-router@4.0.0-beta.7/dist/vue-router.global.js"></script>
    <title>App</title>
  </head>
  <body>
    <div id="app">
      <p>
        <router-link to="/foo">foo</router-link>
        <router-link to="/bar">bar</router-link>
      </p>
      <router-view></router-view>
    </div>
    <script>
      const Foo = {
        template: "<div>foo</div>"
      };
      const Bar = {
        template: "<div>bar</div>",
        beforeEnter(to, from, next) {
          console.log(to, from);
          next();
        },
        beforeRouteUpdate(to, from, next) {
          console.log(to, from);
          next();
        },
        beforeRouteLeave(to, from, next) {
          console.log(to, from);
          next();
        }
      };
      const routes = [
        {
          path: "/foo",
          component: Foo
        },
        {
          path: "/bar",
          component: Bar
        }
      ];
      const router = VueRouter.createRouter({
        history: VueRouter.createWebHistory(),
        routes
      });

      const app = Vue.createApp({});
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>

to add the navigation guards to our Bar component.

The beforeRouteEnter method is called before that renders the component is confirmed.

It doesn’t have access to this component instance since it hasn’t been created yet.

The beforeRouterUpdate method is called when the route that renders the component has changed.

For instance, when a route with dynamic parameters changed, then it’ll be run.

It has access to the this component instance.

beforeRouterLeave is called when the route that renders the component is about to be navigated from.

It also has access to the this component instance.

Conclusion

We can add navigation guard hooks in various locations with Vue Router 4.

Categories
Vue 3

Vue Router 4–In-Component Guards, Route Meta Fields, and Transitions

Vue Router 4 is in beta and it’s subject to change.

To build a single page app easily, we got to add routing so that URLs will be mapped to components that are rendered.

In this article, we’ll look at how to use Vue Router 4 with Vue 3.

In-Component Guards

The beforeRouteEnter guard is run before the route that renders the component is confirmed

Therefore, it doesn’t have access to this .

However, we can access it within the next callback.

To do that, we write:

beforeRouteEnter(to, from, next) {
  next(vm => {
    // ...
  })
}

The vm has the route component’s instance.

The is the only route that supports passing in a callback for next .

We can use this directly in the beforeRouterUpdate and beforeRouterLeave methods, so passing in a callback to next isn’t supported.

So we can just write:

beforeRouteUpdate (to, from, next) {
  this.name = to.params.name
  next()
}

or:

beforeRouteLeave (to, from, next) {
  const answer = window.confirm('Do you really want to leave?')
  if (answer) {
    next()
  } else {
    next(false)
  }
}

beforeRouteLeave is usually used to prevent the user from accidentally leaving the route.

Navigation Resolution Flow

The steps for navigation are as follows:

  1. Navigation triggered.
  2. Call beforeRouteLeave guards in deactivated components.
  3. Call global beforeEach guards.
  4. Call beforeRouteUpdate guards in reused components.
  5. Call beforeEnter in route configs.
  6. Resolve async route components.
  7. Call beforeRouteEnter in activated components.
  8. Call global beforeResolve guards.
  9. Navigation confirmed.
  10. Call global afterEach hooks.
  11. DOM updates are triggered.
  12. Run callbacks passed to next in beforeRouteEnter guards with instantiated instances.

Route Meta Fields

We can add route meta fields with the meta property.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vue-router@4.0.0-beta.7/dist/vue-router.global.js"></script>
    <title>App</title>
  </head>
  <body>
    <div id="app">
      <p>
        <router-link to="/foo">foo</router-link>
        <router-link to="/bar">bar</router-link>
      </p>
      <router-view></router-view>
    </div>
    <script>
      const Foo = {
        template: "<div>foo</div>"
      };
      const Bar = {
        template: "<div>bar</div>"
      };
      const routes = [
        {
          path: "/foo",
          component: Foo
        },
        {
          path: "/bar",
          component: Bar,
          meta: { requiresAuth: true }
        }
      ];
      const router = VueRouter.createRouter({
        history: VueRouter.createWebHistory(),
        routes
      });

      router.beforeEach((to, from, next) => {
        console.log(to.meta.requiresAuth);
        next();
      });

      const app = Vue.createApp({});
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>

to add a meta property to our routes array entry.

We have an object with the requiresAuth property.

Then in the beforeEach callback, we can access it with the to.meta.requiresAuth property.

Transitions

We can add transitions for route changes with Vue Router.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vue-router@4.0.0-beta.7/dist/vue-router.global.js"></script>
    <title>App</title>
    <style>
      .fade-enter-active,
      .fade-leave-active {
        transition: opacity 0.5s;
      }
      .fade-enter,
      .fade-leave-to {
        opacity: 0;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <p>
        <router-link to="/foo">foo</router-link>
        <router-link to="/bar">bar</router-link>
      </p>
      <router-view v-slot="{ Component }">
        <transition name="fade">
          <component :is="Component" />
        </transition>
      </router-view>
    </div>
    <script>
      const Foo = {
        template: "<div>foo</div>"
      };
      const Bar = {
        template: "<div>bar</div>"
      };
      const routes = [
        {
          path: "/foo",
          component: Foo
        },
        {
          path: "/bar",
          component: Bar
        }
      ];
      const router = VueRouter.createRouter({
        history: VueRouter.createWebHistory(),
        routes
      });

      const app = Vue.createApp({});
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>

to add transitions to our app.

We add the router-view and access the Component slot prop to get the component that’s displayed through the router-view .

Then we can pass that to the component component inside the router-view .

We set the name prop so that we can set the prefix of the CSS classes we add for the animation.

Between the style tags, we have the styles for various stages of the animation.

The stages of the transitions are listed at https://v3.vuejs.org/guide/transitions-overview.html#class-based-animations-transitions.

Conclusion

We can add in-component guards and route transitions with Vue Router 4.

The way we add route transitions is different from Vue Router 3.

Categories
Vue 3

Vue Router 4–Router Aliases and Route Props

Vue Router 4 is in beta and it’s subject to change.

To build a single page app easily, we got to add routing so that URLs will be mapped to components that are rendered.

In this article, we’ll look at how to use Vue Router 4 with Vue 3.

Alias

A redirect means that when a user visits /a , then the URL is replaced by /b and matched as /b .

An alias of /a as /b means that when the user visits /b , the URL stays as /b , but it’ll be matched as if the user is visiting /a .

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vue-router@4.0.0-beta.7/dist/vue-router.global.js"></script>
    <title>App</title>
  </head>
  <body>
    <div id="app">
      <p>
        <router-link to="/foo">foo</router-link>
        <router-link to="/bar">bar</router-link>
        <router-link to="/baz">baz</router-link>
      </p>
      <router-view></router-view>
    </div>
    <script>
      const Foo = {
        template: "<div>foo</div>"
      };
      const Bar = {
        template: "<div>bar</div>"
      };
      const routes = [
        {
          path: "/foo",
          component: Foo
        },
        {
          path: "/bar",
          component: Bar,
          alias: "/baz"
        }
      ];
      const router = VueRouter.createRouter({
        history: VueRouter.createWebHistory(),
        routes
      });
      const app = Vue.createApp({});
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>

We have the alias property with the path we want to map to the Bar component.

Then when we click on the baz link, we see bar displayed.

Passing Props to Route Components

We can pass props to route components.

This way, we won’t have to couple the route parameters with the component.

Instead of using the this.$router.currentRoute.value.params property, we just use props to get the route parameters.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vue-router@4.0.0-beta.7/dist/vue-router.global.js"></script>
    <title>App</title>
  </head>
  <body>
    <div id="app">
      <p>
        <router-link to="/foo">foo</router-link>
        <router-link to="/bar/1">bar</router-link>
      </p>
      <router-view></router-view>
    </div>
    <script>
      const Foo = {
        template: "<div>foo</div>"
      };
      const Bar = {
        template: "<div>bar {{id}}</div>",
        props: ["id"]
      };
      const routes = [
        {
          path: "/foo",
          component: Foo
        },
        {
          path: "/bar/:id",
          component: Bar,
          props: true
        }
      ];
      const router = VueRouter.createRouter({
        history: VueRouter.createWebHistory(),
        routes
      });
      const app = Vue.createApp({});
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>

We add the props property to the routes entry and set it to true .

And we have the :id URL parameter placeholder in the route.

Then we add the props property of Bar to take the id prop.

The id prop is displayed in Bar ‘s template.

This way, we can get the id prop from the route parameter.

Also, we have the router-link with the to prop set to '/bar/1' .

This way, we see that 1 as the value of id in the Bar component.

Conclusion

We can add alias and pass route URL parameters as props instead of accessing the value from the $router object in Vue Router 4.

Categories
Vue 3

Vue Router 4–Route Props and History Mode

Vue Router 4 is in beta and it’s subject to change.

To build a single page app easily, we got to add routing so that URLs will be mapped to components that are rendered.

In this article, we’ll look at how to use Vue Router 4 with Vue 3.

Route Props Object Mode

We can set the props property of the routes to accept props.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vue-router@4.0.0-beta.7/dist/vue-router.global.js"></script>
    <title>App</title>
  </head>
  <body>
    <div id="app">
      <p>
        <router-link to="/foo">foo</router-link>
        <router-link to="/bar">bar</router-link>
      </p>
      <router-view></router-view>
    </div>
    <script>
      const Foo = {
        template: "<div>foo</div>"
      };
      const Bar = {
        template: "<div>bar {{id}}</div>",
        props: ["id"]
      };
      const routes = [
        {
          path: "/foo",
          component: Foo
        },
        {
          path: "/bar",
          component: Bar,
          props: { id: 1 }
        }
      ];
      const router = VueRouter.createRouter({
        history: VueRouter.createWebHistory(),
        routes
      });
      const app = Vue.createApp({});
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>

We have the props property in the routes entry with an object:

{ id: 1 }

The id prop is passed in automatically if we navigate to /bar .

The router-link has the /bar as the value of the to attribute.

And when we click on the bar link, we see bar 1 displayed.

The 1 is from the props.

This way of passing route props is useful for static route props.

Route Props Function Mode

The props property can also have a function as its value.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vue-router@4.0.0-beta.7/dist/vue-router.global.js"></script>
    <title>App</title>
  </head>
  <body>
    <div id="app">
      <p>
        <router-link to="/foo">foo</router-link>
        <router-link to="/bar?id=1">bar</router-link>
      </p>
      <router-view></router-view>
    </div>
    <script>
      const Foo = {
        template: "<div>foo</div>"
      };
      const Bar = {
        template: "<div>bar {{id}}</div>",
        props: ["id"]
      };
      const routes = [
        {
          path: "/foo",
          component: Foo
        },
        {
          path: "/bar",
          component: Bar,
          props: (route) => ({ id: route.query.id })
        }
      ];
      const router = VueRouter.createRouter({
        history: VueRouter.createWebHistory(),
        routes
      });
      const app = Vue.createApp({});
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>

We have a function that takes the route object as the parameter.

And we get the query parameter from the query property.

We just return an object with the key being the key of the query parameter.

The value is the query parameter value.

The router-link for the /bar route has the id query parameter.

Now when we click on the bar link, we see bar 1 displayed.

HTML5 History Mode

Hash mode is the default way of mapping routes.

It uses the URL to simulate a full URL so that the page won’t be reloaded when the URL changes.

With history mode, we remove the hash sign so that the URL looks like any other URL we see.

To make HTML5 mode work with various servers, we may have to configure it so that the browser stays in the app when we go to a different URL.

For example, in Apache, we write:

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]
</IfModule>

to make all URL maps to index.html so that index.html of our Vue app is loaded when any request is made.

Conclusion

We can pass route props in various ways with Vue Router 4.

If we use history mode with our Vue 3 app, then we may have to configure our web server to go to our app when any request is made.