Vue || How To Create A 404 Not Found Route With Dynamic Nested Routes With Vue Router Using Vue
The following is a module which demonstrates how to create a 404 not found route with dynamic nested routes via Vue Router using Vue.
Routes can be checked using Navigation Guards. This page will demonstrate verifying nested routes using the Per Route Guard.
This will demonstrate how to create a route that catches all non existing routes with Vue Router. It will also demonstrate how to use navigation guards to make sure nested dynamic routes exists.
Note: The examples below assumes your project is already set up and configured. For an overview on how to get started with Vue and Vue Router, visit the official documentation.
1. Router Index File
The example below is a sample router index.js file. This file sets up the routes in the project.
There are 4 routes: a ‘Home‘ page, a ‘DestinationDetails‘ page, a ‘ExperienceDetails‘ page, and a ‘NotFound‘ page.
The ‘ExperienceDetails‘ page is a nested child route.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
// ============================================================================ // Author: Kenneth Perkins // Date: Dec 25, 2020 // Taken From: http://programmingnotes.org/ // File: index.js // Description: Router index.js file // ============================================================================ import { createWebHistory, createRouter } from "vue-router"; const routes = [ { path: "/", name: "Home", component: () => import(/* webpackChunkName: "Home" */ "../views/Home.vue"), }, { path: "/destination/:slug", name: "DestinationDetails", props: true, component: () => import(/* webpackChunkName: "DestinationDetails" */ "../views/DestinationDetails.vue"), children: [{ path: ":experienceSlug", name: "ExperienceDetails", props: true, component: () => import(/* webpackChunkName: "ExperienceDetails" */ "../views/ExperienceDetails.vue") }], beforeEnter: (to, from, next) => { // Verify the route exists by checking the API let exists = false; // If this is a parent route, check if it exists if (to.params.slug) { // Do something to check if this is valid exists = true; } // If this is a child route, check if it exists if (to.params.experienceSlug) { // Do something to check if this is valid exists = true; } // If the route exists, navigate to it if (exists) { next(); } else { next({name: "NotFound"}); } } }, { path: "/404", name: "NotFound", component: () => import(/* webpackChunkName: "NotFound" */ "../views/NotFound.vue") }, { path: "/:catchAll(.*)", redirect: {name: "NotFound"} } ]; const router = createRouter({ history: createWebHistory(), routes }); export default router; // http://programmingnotes.org/ |
The DestinationDetails route is a dynamic route that accepts props. In this example, the route.params will be set as the component props. In this component, the ‘slug‘ prop is used as the dynamic route path identifier.
The ExperienceDetails nested child route is also a dynamic route. In this component, the ‘experienceSlug‘ prop is used as the dynamic route path identifier.
Using the beforeEnter navigation guard, we can check if the route is valid by checking the dynamic route path identifier. If the route is not valid, the ‘NotFound‘ route is navigated to.
The NotFound route catches all non existing routes.
2. Home Page
The example below is the ‘Home‘ route page. This demonstrates how to navigate to the DestinationDetails route using the dynamic props.
It uses router-link to navigate to the specific route.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<!-- // ============================================================================ // Author: Kenneth Perkins // Date: Dec 25, 2020 // Taken From: http://programmingnotes.org/ // File: Home.vue // Description: Home page that shows all destinations // ============================================================================ --> <template> <section> <h1> All Destinations </h1> <div> <section v-for="destination in destinations" :key="destination.id"> <router-link :to="getNavigationPath(destination)" > <h2>{{destination.name}}</h2> </router-link> <figure> <router-link :to="getNavigationPath(destination)"> <img :src="destination.image" :alt="destination.name"> </router-link> </figure> </section> </div> </section> </template> <script> export default { data() { return { destinations: api.destinations } }, methods: { getNavigationPath(destination) { return { name: 'DestinationDetails', params: { id: destination.id, slug: destination.slug } }; } } }; </script> <style scoped> </style> <!-- http://programmingnotes.org/ --> |
3. Destination Details Page
The example below is the ‘DestinationDetails‘ route page. This demonstrates how to display information about the destination and navigate to the ExperienceDetails route using the dynamic props passed to it.
It uses router-link to navigate to the specific route, and uses router-view to display the nested content from the navigated child route.
When calling the child component, it will automatically receive the parent ‘slug‘ dynamic route path identifier as a parameter.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
<!-- // ============================================================================ // Author: Kenneth Perkins // Date: Dec 25, 2020 // Taken From: http://programmingnotes.org/ // File: DestinationDetails.vue // Description: Displays information about a destination // ============================================================================ --> <template> <section> <div> <h1>{{destination.name}}</h1> <div"> <img :src="destination.image" :alt="destination.name"> <p>{{ destination.description }}</p> </div> </div> <section> <h2> Top experiences in {{destination.name}} </h2> <div> <article v-for="experience in destination.experiences" :key="experience.slug"> <router-link :to="getNavigationPath(experience)"> <img :src="experience.image" :alt="experience.name"> <span> {{experience.name}} </span> </router-link> </article> </div> <router-view :key="$route.path" /> </section> </section> </template> <script> export default { data() { return { } }, props: { slug: { type: String, required: true } }, computed: { destination() { return api.destinations.find( destination => destination.slug == this.slug ); } }, methods: { getNavigationPath(experience) { return { name: 'ExperienceDetails', params: { experienceSlug: experience.slug }, hash: '#experience', }; } } } </script> <style scoped> </style> <!-- http://programmingnotes.org/ --> |
4. Experience Details Page
The example below is the ‘ExperienceDetails‘ route page. This demonstrates how to display information about the destination experience using the dynamic props passed to it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
<!-- // ============================================================================ // Author: Kenneth Perkins // Date: Dec 25, 2020 // Taken From: http://programmingnotes.org/ // File: ExperienceDetails.vue // Description: Displays information about an experience // ============================================================================ --> <template> <section> <h2>{{experience.name}}</h2> <div> <img :src="experience.image" :alt="experience.name"> <p id="experience"> {{experience.description}} </p> </div> </section> </template> <script> export default { props: { slug: { type: String, required: true, }, experienceSlug: { type: String, required: true, } }, computed: { destination() { return api.destinations.find( destination => destination.slug == this.slug ); }, experience() { return this.destination.experiences.find( experience => experience.slug == this.experienceSlug ); } } } </script> <style scoped> </style> <!-- http://programmingnotes.org/ --> |
5. Not Found (404) Page
The example below is the ‘NotFound‘ route page. This page catches all non existing routes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<!-- // ============================================================================ // Author: Kenneth Perkins // Date: Dec 25, 2020 // Taken From: http://programmingnotes.org/ // File: NotFound.vue // Description: Not found (404) page // ============================================================================ --> <template> <section> <h1>Not Found</h1> <p>Oops we couldn't find that page. Try going <router-link :to="{name: 'Home'}"> home </router-link> </p> </section> </template> <script> export default { data() { return {} }, methods: {} } </script> <style scoped> </style> <!-- http://programmingnotes.org/ --> |
QUICK NOTES:
The highlighted lines are sections of interest to look out for.
The code is heavily commented, so no further insight is necessary. If you have any questions, feel free to leave a comment below.
Leave a Reply