In my Vue.js project, I want to display my 404 page, if a route parameter is invalid. For now, I'm using the following code for that:
this.$router.replace({ path: '/404' });
Is there a way to do that without modifying the URL? I want the user to still be able to copy the browser's original URL line. Is there some kind of a silent: true
parameter?
In my Vue.js project, I want to display my 404 page, if a route parameter is invalid. For now, I'm using the following code for that:
this.$router.replace({ path: '/404' });
Is there a way to do that without modifying the URL? I want the user to still be able to copy the browser's original URL line. Is there some kind of a silent: true
parameter?
- Might be helpful: github./vuejs/vue-router/issues/977 – Farkhat Mikhalko Commented Mar 4, 2020 at 10:44
- @FarkhatMikhalko Unfortunately no. I already found that discussion. In my case, the redirect should be initiated from the view itself. – André Reichelt Commented Mar 4, 2020 at 10:52
4 Answers
Reset to default 7With vue-router
, the URL is the source of truth. If the URL changes, so does the rendering. You can't "pause" the router. (This is a flaw in vue-router
that has been bugging me for ages, but I digress.)
You just have to display the 404 page without modifying the route. Have some display404
data property in your root ponent that you can set to display the 404 page manually in the template instead of the <router-view>
, e.g:
<div>
<my-404-page v-if="display404"/>
<router-view v-else/>
</div>
To display the 404 page from any ponent:
this.$root.display404 = true
Of course this is just a basic example to demonstrate what I mean, you might want to use Vuex to share the state, or use an event bus, or you can display the 404 page in some other way that works for you, etc.
This was fixed in Vue Router 4 which you can see on the second example in the docs.
Build your NotFound
route like this:
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
ponent: NotFound
},
Then you can use a beforeEnter
navigation guard on your dynamic Vue like so:
// In your router/index.js file...
{
path: 'users/:id',
name: 'User Detail',
ponent: UserDetail,
beforeEnter(to, from) {
// See if that query exists in your data...
const exists = data.users.find(
user => user.id === parseInt(to.params.id)
)
if (!exists) {
// THE IMPORTANT PART
// Return your not found view...
return {
name: 'NotFound',
// Match the path of your current page and keep the same url...
params: { pathMatch: to.path.split('/').slice(1) },
// ...and the same query and hash.
query: to.query,
hash: to.hash,
}
}
}
}
Haven't tested this in a Component yet, but I'd assume it'd be the same logic in the beforeRouteEnter
navigation guard.
Not 100% sure what you are asking, but is either of these any help?
A catch all route: From Vue.js docs "Catch all route"
Or if you are managing a response form a call (method/fetch/ etc): Use a bination of try/catch and a "loading" data value to change the display or what ponent is loaded.
Based on Decade Moon's solution, I did the following:
main.js
import Error404 from './views/error/404.vue'
Vue.ponent('error-404', Error404)
404.vue
<template>
<div>
<h1>Page not found</h1>
<p>Whatever...</p>
</div>
</template>
<script>
export default {
name: 'Page not found'
}
</script>
router --> index.js
const PageNotFound = () => import('@/views/error/404')
function configRoutes() {
return [
{
path: '/',
name: 'Home',
ponent: TheContainer,
children: [
// ...
{
path: '404',
name: 'Page not found',
ponent: PageNotFound,
alias: '*'
}
]
}
]
}
My Page which should display the 404 error
<template>
<div class="animated fadeIn" v-if="clientSettings">
...
</div>
<error-404 v-else></error-404>
</template>
<script>
export default {
name: 'Test',
data() {
return {
clientSettings: null
};
},
async created() {
this.setClientConfig();
},
watch: {
'$route.params.id': function (id) { this.setClientConfig(id);}
},
methods: {
setClientConfig(id) {
if (!id) {
id = this.$route.params.id;
// Redirect to the first valid list, if no parameter is proviced
if (!id) {
this.$router.push({ name: 'Test', params: { id: this.$root.clientConfiguration[0].name } });
return;
}
}
// Set client settings
this.clientSettings = this.$root.clientConfiguration.find(cc => cc.name === id);
// This will return null, if no entry was found, therefore the template will jump into the v-else
}
}
}
</script>