tildeio / router.js

MIT License
1.35k stars 159 forks source link

transitionTo and handleURL fail silently if `triggerEvent` is not provided. #333

Open MichaelPote opened 2 years ago

MichaelPote commented 2 years ago

If you create a new Router() instance and do not create a triggerEvent function on it (eg. router.triggerEvent = function(){}; ) then when you try to use the router via transitionTo or handleURL your route handler functions will not be called, the hidden optional logging function provided as a constructor argument doesnt log any errors, nor does the javascript engine itself. Only the returned Intent object shows a this.triggerEvent is not a function error without any additional information.

Doing a global text search through the entire codebase for "triggerEvent" only returns a function definition in router.d.ts which implies that the function should be implemented inside the router.es.js module but it isnt.

Also reading through the entire README yields no mention of triggerEvent or anything to point towards an issue here.

MichaelPote commented 2 years ago

I've found another undocumented function: didTransition is not defined either and causes all transitions to be aborted with yet another silent error.

Am I missing something? It seems this project was not meant to be used outside of Ember?

MichaelPote commented 2 years ago

After a lot of headbashing, I found some code in the Unit Tests which should be explained in the Readme. The Router class provided is not ready to use, you need to extend the class with your own concrete Router class which implements vital functionality. Especially the event handling code which should at least be provided by default.

For anyone stuck on this issue, this is how to properly subclass the Router and provide yourself with a working instance:

import AbstractRouterJS from "router_js";

class RouterJS extends AbstractRouterJS {
    routeDidChange() {}
    routeWillChange() {}
    didTransition() {}
    willTransition() {}

    triggerEvent(handlerInfos,ignoreFailure,name,args) {

            if (!handlerInfos) {
                if (ignoreFailure) {
                    return;
                }
                throw new Error("Could not trigger event '" + name + "'. There are no active handlers");
            }

            let eventWasHandled = false;

            for (let i = handlerInfos.length - 1; i >= 0; i--)
            {
                let currentHandlerInfo = handlerInfos[i],
                    currentHandler = currentHandlerInfo.route;

                // If there is no handler, it means the handler hasn't resolved yet which
                // means that we should trigger the event later when the handler is available
                if (!currentHandler)
                {
                    if (currentHandlerInfo.routePromise)
                    {
                        currentHandlerInfo.routePromise.then(function (resolvedHandler)
                        {
                            if (resolvedHandler.events.hasOwnProperty(name))
                            {
                                resolvedHandler.events[name].apply(resolvedHandler, args);
                            }
                        });
                    }
                    continue;
                }

                if (currentHandler.events && currentHandler.events[name]) {
                    if (currentHandler.events[name].apply(currentHandler, args) === true) {
                        eventWasHandled = true;
                    } else {
                        return;
                    }
                }
            }

            // In the case that we got an UnrecognizedURLError as an event with no handler,
            // let it bubble up
            if (name === 'error' && (args[0] && args[0].name === 'UnrecognizedURLError')) {
                throw args[0];
            } else if (!eventWasHandled && !ignoreFailure) {
                throw new Error("Nothing handled the event '" + name + "'.");
            }

    }

    getRoute(name) {
        throw new Error("getRoute is not defined!");
    }

    updateURL(newUrl) {
        console.log("Updating browser URL to ", url);
        window.history.pushState('', '', url);
    }

    replaceURL(url) {
        console.log("Replace browser URL with ", url);
        window.history.replaceState('', '', url);
    }
}
amiramitai commented 1 year ago

I ran into this too. I found that the following hack gave me the trigger and it satisfied my purposes.

router.triggerEvent = (handlerInfos, ignoreFailure, name, args) => {}
router.routeWillChange = (newTransition) => {};
router.willTransition = (oldRouteInfos, routeInfos, newTransition) => {};
router.routeDidChange = (newTransition) => {};
router.didTransition = (routeInfos) => {
    let {name, params} = routeInfos[0];
    handlers[name].model(params);
};