```html
<template>
<div class="post">
- <div v-if="loading" class="loading">
- Loading...
- </div>
+ <div v-if="loading" class="loading">Loading...</div>
- <div v-if="error" class="error">
- {{ error }}
- </div>
+ <div v-if="error" class="error">{{ error }}</div>
<div v-if="post" class="content">
<h2>{{ post.title }}</h2>
return {
loading: false,
post: null,
- error: null
+ error: null,
}
},
created() {
},
watch: {
// call again the method if the route changes
- $route: 'fetchData'
+ $route: 'fetchData',
},
methods: {
fetchData() {
this.post = post
}
})
- }
- }
+ },
+ },
}
```
data() {
return {
post: null,
- error: null
+ error: null,
}
},
beforeRouteEnter(to, from, next) {
},
// when route changes and this component is already rendered,
// the logic will be slightly different.
- beforeRouteUpdate(to, from, next) {
+ async beforeRouteUpdate(to, from) {
this.post = null
- getPost(to.params.id, (err, post) => {
- this.setData(err, post)
- next()
- })
- },
- methods: {
- setData(err, post) {
- if (err) {
- this.error = err.toString()
- } else {
- this.post = post
- }
+ try {
+ this.post = await getPost(to.params.id)
+ } catch (error) {
+ this.error = error.toString()
}
- }
+ },
}
```
The user will stay on the previous view while the resource is being fetched for the incoming view. It is therefore recommended to display a progress bar or some kind of indicator while the data is being fetched. If the data fetch fails, it's also necessary to display some kind of global warning message.
-### Using Composition API
+<!-- ### Using Composition API -->
-TODO:
+<!-- TODO: -->
So how do we access this `meta` field?
+<!-- TODO: the explanation about route records should be explained before and things should be moved here -->
+
First, each route object in the `routes` configuration is called a **route record**. Route records may be nested. Therefore when a route is matched, it can potentially match more than one route record.
For example, with the above route config, the URL `/posts/new` will match both the parent route record (`path: '/posts'`) and the child route record (`path: 'new'`).
-All route records matched by a route are exposed on the `$route` object (and also route objects in navigation guards) as the `$route.matched` Array. We could loop through that array to check all `meta` fields, but Vue Router also provides you a `$route.meta` that is first-level merge of **all `meta`** fields from parent to child. Meaning you can simply write
+All route records matched by a route are exposed on the `$route` object (and also route objects in navigation guards) as the `$route.matched` Array. We could loop through that array to check all `meta` fields, but Vue Router also provides you a `$route.meta` that is a non-recursive merge of **all `meta`** fields from parent to child. Meaning you can simply write
```js
-router.beforeEach((to, from, next) => {
+router.beforeEach((to, from) => {
// instead of having to check every route record with
// to.matched.some(record => record.meta.requiresAuth)
- if (to.meta.requiresAuth)) {
+ if (to.meta.requiresAuth && !auth.isLoggedIn()) {
// this route requires auth, check if logged in
// if not, redirect to login page.
- if (!auth.isLoggedIn()) {
- next({
- path: '/login',
- // save the location we were at to come back later
- query: { redirect: to.fullPath }
- })
- } else {
- next()
+ return {
+ path: '/login',
+ // save the location we were at to come back later
+ query: { redirect: to.fullPath },
}
- } else {
- next() // make sure to always call next()!
}
})
```
+
+## TypeScript
+
+It is possible to type the meta field by extending the `RouteMeta` interface:
+
+```ts
+declare module 'vue-router' {
+ interface RouteMeta {
+ // is optional
+ isAdmin?: boolean
+ // must be declared by every route
+ requiresAuth: boolean
+ }
+}
+```