Earlier this week, I talked about mapping RESTful resource URIs onto URL parameters and server-side events. When developing a thick-client application, much of the same URL routing functionality is required on the client. Only, with the ever-growing complexity of rich user interfaces, routing and partial page rendering on the client becomes much more difficult. Lately, I've been looking at Google's AngularJSas a declarative framework for client-side applications. And, as a follow-up to yesterday's post, I thought I would look at mapping AngularJS routes onto client-side URL parameters and rendering events.
AngularJS comes with a fairly robust routing and history management mechanism. Out of the box, it supports both hash-based routing as well as HTML5 push-state (I've only played with hash-based routing). The route provider is primarily concerned with mapping routes onto template paths and is configured with a series of when() method calls:
123456 |
|
In the above case, the given route causes the given template to be rendered in an "ng-view" AngularJS directive. This approach is great for smaller websites and shallow web applications; but, when you have nested navigation and complex interfaces, the route-to-partial paradigm can quickly become quite limiting.
Fortunately, you don't have to use templates with routes. Instead, you can use routing to resolve client-side render events. In the same way that I resolved resource URIs onto server-side events, we can resolve client-side routes onto a hierarchy of values that defines the state of the rendered page.
The trick to this is understanding that the hash used to define the AngularJS route can contain any arbitrary data (so long as it doesn't conflict with some reserved values). So, for example, rather than passing in a templateUrl, we can pass in event data:
123456 |
|
In this case, we are mapping the route onto the render event "friends.view". This event parameter will then be exposed as part of the $route state. The ":friendID" value, in the route, will also be exposed as part of the $routeParams state.
To see how this can be used to render a page, I've created a light-weight demo with a single Controller that conditionally renders content based on the route-provided render-event.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
|
In this demo, our application controller is listening for changes to the route. When a route is changed (also fires when page loads for the first time), the AppController extracts the route-mapped "action" value and injects it into the scope of the page. This value (and its derivative, renderPath) is then used to conditionally render the page using "ng-switch" directives.
Right now, this page has only one level of nesting. But, ng-switch directives can easily be nested in order to allow for deep-linking of your application. Furthermore, the "ng-switch-when" directive can be combined with the AngularJS "ng-include" directive to lazy-load page partials:
1234567 |
|
On the server-side, events tend to map to resources and the access / mutation of those resources. In a rich, client-side application, however, events are a different beast. Rather than mapping to resources, they map to page state. And, depending on the complexity of your user interface, state may be defined as a complex hierarchy of sections. Fortunately, AngularJS routes make it possible to map routes onto render events which can then be used to drill down through a rendered page.
ewrwer
ReplyDelete