0

I'm trying to catch errors that might occur when parsing route parameters to pass to a component. For instance, component A requires a URL object and the router creates the object from the route parameter before passing it to the component, e.g.

{
  path: "/test/:parameter",
  name: "test",
  component: Test,
  props: (route) => ({
    url: new URL(route.params.parameter),
  }),
},

Vue Router offers the onError function for catching certain kinds of errors and other places have suggested using Vue Router's beforeEach. Unfortunately neither seems to be useful if an error is thrown while generating a route component's props.

The v3 docs of onError suggest that this use case isn't covered:

  • The error is thrown synchronously inside a route guard function;
  • The error is caught and asynchronously handled by calling next(err) inside a route guard function;
  • An error occurred when trying to resolve an async component that is required to render a route.

But it's not exactly clear and it doesn't seem very useful if this use case isn't covered. So how would you do it?

MCVE

I've created a minimal example to demonstrate the behaviour. There are three links:

  1. "Valid" - should work fine
  2. "Invalid" - throws an exception when creating the props
  3. "Does not exist" - should throw an error because the route doesn't exist

"Invalid" definitely throws an uncaught error (shown in the view), but neither 2. nor 3. write a log message into the console as expected.

5
  • "So how would you do it?", prop validation within the component. see "propG" example in the documentation that uses custom validator function. this can be used to test for valid URL.
    – yoduh
    Commented Sep 6, 2024 at 13:54
  • Can you clarify what's the intended behaviour? Do you want "Invalid" to be handled by router error handler the same way as "Does not exist"? I doubt it's possible without extra redirects or else. "props" takes place when the navigation has already been completed, it's not router but render error, this is what you see, it comes from router view instance. Proceed from the fact that props needs to be error-free, or handle the logic at component level. Route props are less useful with composition api. You can write your own param mapping in comp setup with the level of flexibility you need Commented Sep 6, 2024 at 13:55
  • "Does not exist" is unimportant, it's just another example of routing errors not being caught in onError. The intended behaviour is that error in onError: ... is shown in the console.
    – Druckles
    Commented Sep 6, 2024 at 14:03
  • @yoduh You shouldn't need a custom validator; type: URL should be enough on the prop. But then you have to parse it in the router and then you can't catch the error anymore.
    – Druckles
    Commented Sep 6, 2024 at 14:07
  • @Druckles I see. Use Vue's onError in addition to router's then. You can handle it in a RouterView's parent, for instance. Or move logic from "props" to a place where an error makes sense to be handled. The relevant part github.com/vuejs/router/blob/… . The error happens on RouterView render and it's not caught by itself, it's technically not a navigation error, so it works as expected. You could open a feature issue, but I'd expect it to be discarded by the owner. Commented Sep 6, 2024 at 14:17

1 Answer 1

0

Create transformRoutes function where you:

  1. parseProps in beforeEach, if errored, create an error prop
  2. return the parsed props on actual props' call
  3. if the props contains an error, return some error component

(of course it should be extended to handle all possible route configurations)

main.ts:

import { transformRoutes } from "./transform-routes";
const router = createRouter({
  history: createWebHistory(),
  routes: transformRoutes([ routes here ])
});

transform-routes.js:

import Error from './views/Error.vue';

export function transformRoutes(routes) {
    routes.forEach(route => {

        let _props, _component, props = {};

        [_props, route.props] = [route.props, () => {
            const out = props;
            props = {};
            return out;
        }];

        [_component, route.component] = [route.component, async () => {
            if (props.error) {
                return Error;
            }
            return _component;
        }];

        route.beforeEnter = (...args) => {
            try {
                props = _props(...args);
            } catch (e) {
                props = { error: e.message };
            }
        }
    });
    routes.children && transformRoutes(routes.children);
    return routes;
}

Error.vue:

<script setup>
defineProps({
  error: String,
});
</script>
<template>
  <div class="error">
    {{ error }}
    <div><button @click="$router.go(-1)">Go back</button></div>
  </div>
</template>
<style scoped>
.error {
  background: #fee;
  padding: 32px;
  border: 1px solid red;
  margin: 32px;
}
</style>

enter image description here

3
  • args in beforeEnter (NavigationGuardWithThis) appear to be totally different from those expected by props (RouteLocationNormalized).
    – Druckles
    Commented Sep 9, 2024 at 13:36
  • @Druckles it could have different types, but the data is the same (at least the params), you could adjust, it was i quick prototype which actually took some time and efforts. if after some adjustments it will solve your problem, don't hesitate to upvote and accept, thanks Commented Sep 9, 2024 at 13:38
  • Alright, I've tried it again and it does work. Although it's clever, it's very hacky and given it doesn't catch other routing errors (like "Does not exist"), it's not going to do unfortunately. Thank you anyway.
    – Druckles
    Commented Sep 10, 2024 at 8:52

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.