TLDR:
- It's possible to use Global Navigation Guards with Nuxt by using Middleware
- In the Middleware, you have access to the context, by which you can directly access the Vue Router.
- By adding a Global Navigation Guard, that code will run every time your router is used, leading to unintended consequences
- Instead, use in-component Navigation Guards within your pages
I'm still very new to Vue and Nuxt, but as we are using it at my job I'm trying to pick it up as I go. We are building a task management system similar to Pivotal Tracker or Trello. We have Lists, and within these Lists we have Tasks, and one Task showing its TaskDetails. By clicking on the Tasks in the List you can change which TaskDetail is shown. I was faced with what I initially thought was an easy task.
TASK: When a user is editing a text box within the TaskDetail, ensure the user does not accidently navigate away from the page (and losing their edit) by adding a confirm popup.
Enter Navigation Guards (Navigation Guards | Vue Router) , which are "primarily used to guard navigations either by redirecting it or canceling it." By using a Global Navigation Guard, I could ensure that anytime the Vue Router changes the route (when someone clicks on a different Task), I could check if they are editing a text box and then use a confirm popup.
Something like this:
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
if(confirm("Are you sure?")) {
next()
} else {
next(false)
}
})
Problem: We are using Nuxt, which sets up the Vue-router automatically based on your file structure. But there must be a way to use a Global Navigation Guard with Nuxt, right?
I googled, and all I could find addressing this was one lone Stack Overflow question, which had no answer but at least a useful comment about how middleware was designed for exactly that.
What is middleware in Nuxt?
"Middleware lets you define custom functions that can be run before rendering either a page or a group of pages." (Routing - Nuxt.js) Also importantly, the Middleware has access to the Context (API: The Context - Nuxt.js), which has access to the store, which has access to the Vue Router!
So my idea is now simple: create a middleware that accesses the router directly, add a Global Navigation Guard so that whenever the router changes pages it confirms with the user if they really want to move. Let's do it!
For my middleware I created this basic file
in check-before-move.js
export default function ({ store }) {
store.app.router.beforeEach((to, from, next) => {
if(confirm("Are you sure?")) {
next()
} else {
next(false)
}
})
}
Then added that to the config for nuxt
in nuxt.config.js
router: {
middleware: "check-before-move"
}
This lead to a sticky little problem. Can you spot it? If my middleware really does run every time the router runs, what is going to happen?
That's right, every time the router runs I'm adding ANOTHER Global Navigation Guard. My user has to confirm once, then twice, then three times..etc every time they navigate to different Tasks. Remember, that middleware file is running from start to finish every time the router moves. And each time that middleware file runs, we are doing another beforeEach
. It's obvious looking at it now, but it took me a long time to figure out at the time!
So my dumb brain said, "Oh ok I just need to add a check to see if I've set it up already", and I immediately changed my code to:
in check-before-move.js
export default function ({ store }) {
let setup = false
if(!setup) {
store.app.router.beforeEach((to, from, next) => {
setup = true
if(confirm("Are you sure?")) {
next()
} else {
next(false)
}
})
}
}
That, as you can imagine, also did not work (read above where I said the code runs from start to finish every time and you'll see why my master plan failed)
So as we can see, YES you can add a Global Navigation Guard in Nuxt by using Middleware, but NO you should not do it(or at least be very sure about what you're doing before implementing it).
Afterword
For anyone else who may be trying to figure out how to implement the same feature in their own project and is suffering like I did, I'll tell you how I managed to implement a solution. Instead of going global, I introduced an In-component Guard. Be careful though, those In-component Guards only work on PAGES, not components (even though Pages are Components but Nuxt has opinions on these matters), so look into In-component Guards here (Navigation Guards | Vue Router)
Good luck, and happy coding!
(Note: I am new to Vue/Nuxt as I stated before, so if you spot any mistakes, feel free to point them out!)
Top comments (7)
Well :)
Its quite simple.. imagine that
middleware
is (!)router.beforeEach
Where does
next()
come from?For me, even a simple code like this:
throws an error saying: "Reference error: next is not defined".
Sorry, that was taken just as an example from the article, but agree, can be confusing.
In docs you can find this example of
middleware/auth.js
Hi, I stumbled upon this issue on Stack Overflow that fits in what I'm trying to achieve: stackoverflow.com/questions/491866...
The first thing that came to my mind is to use the navigation guards to check and load the right component.
Does navigation guards work on page load/refresh?
Hi! I believe it depends on the navigation guard.
Component navigation guards like beforeRouteLeave or beforeRouteUpdate will not run on page load/refresh. However, something like beforeRouteEnter will run on page load/refresh.
The navigation guard beforeEnter should also run on page load/refresh.
Hope that helps!
@husteadrobert thanks for the article. Would love to see how you implement non-global (page-based) navigation guards in Nuxt (not Vue).
Nice article, I think for someone that needs to do an auth ceck using middleware should be fine, since I need to check for the auth status of every page.