A little while ago I read and article about routing in ember which was a big surprise to me, not so much their approach but that people weren't already doing it. At the moment it seems that a router is being used across a program and individual components are listening for specific routes. The issue with this is that you end up with your routs being defined across a number of components so if you update your URL structure then you're going to have problems hunting down all those routes that were defined.
So have a read of the routing in Ember.js article then come back because we're going to take it one step further.
What I currently do is extend the router and call it State.js and then make it a singleton that is available to the whole program. This is the central repository for my state and is also where all the routing will be handled. If a user wants to change the state when clicking a link I can just send the variables that need changing to the state. This will then change it's internal variables, output it's state as a URL and then push that in to the history. The big advantage of this is that instead of declaring the whole URL on that link you only need to know which variables change. For instance if you had several filters that could be applied to a search page you could just call your State.setFilter(...) and have that added. The state then knows to keep the rest of it's filters intact and will generate the new URL with the filters it already has on still in the URL.
This makes it easier to apply URLs where you may have several filters at one. Today most routers handle tree like structures well but are difficult to use when you want to do things like apply size and colour filters on - things which don't have an order and can be expressed many ways. The best way to put this in a URL is outside the path. Usually I like to put whatever I can in the path like structure and then give the rest of the variables as a query string on the end.
Now we've decoupled setting state, but how about reading it or running things from different controls on different states. For that I use the mediator. The router should be setup to accept all the possible routes and can then pass the changes with all the information needed through a mediator. This means that you can also do special processing of the urls in the mediator too if need be. Because we've done this the individual components will only be listening to events that you define and not to URLs that may change.
That is what I currently do, but it has struck me there might be an even better approach and one which I will be experimenting with in future. State is really just a whole lot of variables and a router is a way of setting and getting state back from the URL. This sounds just like a model and a sync. Next time I'm writing a program I'm going to see if I can create a state model with it's sync being the router and then I should be able to bind on changes to that model and send those changes through a mediator to the rest of the program. This means the logic for the binding between state variables and the URL will be pushed in to the sync, or as an interface between the model and the router and the program will only have to worry about the state model. The benefits of this should be that models already have binding magic and so should cut down on code.
Same here. I am already writing a new router :) But state in app instead in url is not the only problem to solve. Next step is to better mimic browser behaviour when doing routing. For example: preserve scroll position on bf, persist forms state in localstorage, async loading, last click win etc. all related to page transitions and more.
ReplyDeleteI have fixed these issues and now I am looking how to put all pieces together.