I ran into a little problem today with a custom enyo router with a slightly different regexp for tokens in dynamic routes.
Since the token regexp is a private protected variable inside a closure, I cannot simply override it by extending the enyo router. As a result I had to clone the native router. At first this seemed harmless apart from being a little fiddly, but today I ran into an interesting side effect.
The closure that contains the definition of the enyo.Router
kind contains an enyo.ready
callback that tells enyo.dispatcher
to listen to the hashchange
event on the window object. This essentially means binding the hashchange
event happens asynchronously, leaving room for subtle race conditions.
To be more specific, when using the native enyo.Router this doesn't appear to be cause any problems, because the router is part of the enyo kernel. This ensures that router initialization and application start-up take place in the following order:
enyo/kernel/Router.js
file gets loadedenyo.ready
callbackenyo.ready
triggers it's callbacksenyo.dispatcher
gets instructed to listen for the hashchange
eventHowever, now that I have been forced to create an entirely new router, rather than extending the original one, the new router is no longer part of the enyo kernel and thus no longer part of the kernel package. Instead it becomes part of the application package. This alters the order in which router initialization and application start-up take place:
enyo/kernel/Router.js
file gets loadedenyo.ready
callbackenyo.ready
triggers it's callbacksenyo.dispatcher
gets instructed to listen for the hashchange
eventenyo.Application.start
, which means the hashchange
is ignored until after myApp.Router.trigger
has alreadu changed the window.location.hash
.
Allthough I can work around this issue, it does make extending and customizing enyo's router a bit fiddly and I'm not sure that's really necessary... A couple of things come to mind that could be implemented to make enyo's router more flexible:
Is this something the enyo team would be willing to look into?
It looks like you're new here. If you want to get involved, click one of these buttons!
Comments
Sure. Currently I've changed the token regexp from:
/\:[a-zA-Z0-9]*/g
to:/\:[a-zA-Z0-9-_]*/g
. Nothing awfully exotic, it just allowed a bit more freedom to naming the tokens in a path.I find
foo/:bar_and_baz/:bar_id/:baz_id
just reads a little easier thanfoo/:barandbaz/:barid/:bazid
The more important modifications I've made to the router are the regexp's to extract the value of a token. I've modified the regexp that extracts the token's value from a url path from:
([a-zA-Z0-9-]*)
to:([a-zA-Z0-9-_.!~*'()%+]*)
This allows me to handle uri encoded values (as implemented by
encodeURIComponent
) in a dynamic routes and decode them inenyo.Router._handleDynamic
).My use-case for this was a search feature, such that routes like
search/:query
can be used to match paths like these:-
"search/john%20doe"
=> query = "john doe"-
"search/cartman%20awesome-o"
=> query = "cartman awesome-o"-
"search/M%26M's"
=> query = "M&M's".enyo.Router
has some public methods, such asenyo.Router.trigger
andenyo.Router.location
that make use of private protected methods such asprepare
andhashDidChange
. So even if the token regexp doesn't strictly *need* to change, these private methods and variables still make it difficult to customize the router without creating a new IIFE with copies of these private methods and variables, creating the race-condition I mentioned as a side-effect.