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.

Categories
Vue 3

Vue Router 4–Redirects

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.

Redirect

We can add redirects to our routes.

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
        },
        {
          path: "/baz",
          redirect: "/bar"
        }
      ];
      const router = VueRouter.createRouter({
        history: VueRouter.createWebHistory(),
        routes
      });
      const app = Vue.createApp({});
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>

We have 3 routes in the routes array.

In the 3rd entry, we have the redirect property to redirect the /baz route to the /bar route.

And we have a router-link that points to the /baz route.

So when we click on it, we still see bar displayed.

The redirection also works with named routes.

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,
          name: "bar"
        },
        {
          path: "/baz",
          redirect: { name: "bar" }
        }
      ];
      const router = VueRouter.createRouter({
        history: VueRouter.createWebHistory(),
        routes
      });
      const app = Vue.createApp({});
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>

We have the name property in the /bar route and we have the redirect property in the /baz route set to:

{ name: "bar" }

So we’ll see that when we click on ‘baz’, we see bar .

We can also redirect with a callback 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>
        <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,
          name: "bar"
        },
        {
          path: "/baz",
          redirect: (to) => "bar"
        }
      ];
      const router = VueRouter.createRouter({
        history: VueRouter.createWebHistory(),
        routes
      });
      const app = Vue.createApp({});
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>

We return 'bar' in the redirect callback so that it goes to /bar when we click on /baz .

And we get the same result.

Conclusion

We can redirect our routes in various ways with Vue Router 4.

Categories
Vue 3

Vue Router 4–Programmatic Navigation

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.

Programmatic Navigation

We can programmatically navigate through routes with Vue Router 4.

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>
        <a href="#" @click.stop="go">Bar</a>
      </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
      });

      const app = Vue.createApp({
        methods: {
          go() {
            this.$router.push("bar");
          }
        }
      });
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>

We have an a element to go to the bar route.

The stop modifier is added to @click to prevent propagation.

In the root Vue instance, we have the this.$router.push method to go to the given route.

We can also write:

this.$router.push({ path: "bar" });

We can do this for named routes:

router.push({ name: 'bar', params: { id: '123' } })

So 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>
        <a href="#" @click.stop="go">Bar</a>
      </p>
      <p>{{ $router.currentRoute.value.params.id }}</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/:id", component: Bar, name: "bar" }
      ];

      const router = VueRouter.createRouter({
        history: VueRouter.createWebHistory(),
        routes
      });

      const app = Vue.createApp({
        methods: {
          go() {
            this.$router.push({ name: "bar", params: { id: "123" }       });
          }
        }
      });
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>

We added the :id URL parameter placeholder and added the name property to it.

So we can call push with the name value instead of the path.

The URL parameter is in the id property.

$router.replace(location, onComplete?, onAbort?)

The $router.replace method is like the $router.push except that it overwrites the previous history entry.

We can add the replace prop to router-link like:

<router-link :to="..." replace>

which is the same as:

router.replace(...)

$router.go(n)

The $router.go method takes a single integer and lets us go how given number of steps forward or backward in the browser history.

A negative integer means we’re going backward.

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>
        <a href="#" @click.stop="goBack">Back</a>
      </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
      });

      const app = Vue.createApp({
        methods: {
          goBack() {
            this.$router.go(-1);
          }
        }
      });
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>

We have the goBack method with the this.$router.go call to go back to the previous route.

The convention of the method follows the History API.

Conclusion

We can navigate to different routes programmatically with Vue Router 4, which works with Vue 3.