Angular JS Routing


Published on

Talk given at AngularJS meeting in London on 13 August 2013.

Published in: Business, Technology
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Angular JS Routing

  1. 1. AngularJS Application Routing Tuesday, 13 August 13
  2. 2. Routes: REST/Servers v. Client Apps •HTTP is stateless, so routes help define application state. •REST emphasizes semantic routes. (GET  /user/dave) But... •There is more state than can be represented in a route (user state, session state) •Client-side apps don’t actually need routes to work (e.g. many jquery-based apps) Tuesday, 13 August 13
  3. 3. Why use routes? Routes: •Make the back button work as expected •Make major (page-like) transitions clear •Allow easy linking •Make testing easier •Encapsulate some state Tuesday, 13 August 13
  4. 4. From Angular-Phonecat: angular.module('phonecat',  ['phonecatFilters',   'phonecatServices']).    config(['$routeProvider',  function($routeProvider)  {    $routeProvider.            when('/phones',  {templateUrl:  'partials/phone-­‐ list.html',      controller:  PhoneListCtrl}).            when('/phones/:phoneId',  {templateUrl:  'partials/ phone-­‐detail.html',  controller:  PhoneDetailCtrl}).            otherwise({redirectTo:  '/phones'}); }]); Basic Routes Tuesday, 13 August 13
  5. 5. .when('/phones/:phoneId',  {templateUrl:  'partials/phone-­‐ detail.html',  controller:  PhoneDetailCtrl}) :phoneId stored in $route.current.params, available controller and resolve functions. Basic Routes Tuesday, 13 August 13
  6. 6. Realistic Routes Structure / – welcome /stream – logged-in home /login – log-in /signup – signup /user/:username – user profile /explore – explore tags /settings – user settings /new-spark – create spark page /spark/cluster?sparkName=&tagName – spark cluster page /search – search /spark/:username/:slug – spark page ...and 11 more routes Tuesday, 13 August 13
  7. 7. Resolve for Data Each item in the resolve map: •Runs before the controller is instantiated •Provides its value to the controller •If it is a promise: •It will (wait to) resolve the promise first •If the promise is rejected, the route errors Using resolve for database objects means •No UI flicker as promises are resolved •Controllers can assume objects exist •Tests are cleaner: pass in data object explicitly Tuesday, 13 August 13
  8. 8. Resolve for Data when('/tag/:tagName',  {  resolve:  {    tagResolve:  ['tag','$route',      function  (tagService,  $route)  {        return  tagService.get($route.current.params.tagName);    }],    sparkClustersResolve:  ['sparkCluster','$route',      function  (sparkClusterService,  $route)  {        return   sparkClusterService.getSparkClustersByTag($route.current.p arams.tagName); }]}}); Tuesday, 13 August 13
  9. 9. Resolve for Data Treat your resolved object like a dependency in the controller: .controller('ViewTagCtrl',  ['$scope','$routeParams','tag', 'sparkCluster','log','$location','user','header','sparkClu stersResolve','tagResolve','currentUserResolve' Use a ‘route controller’, not a ‘view controller’ (ng-­‐controller): when('/tag/:tagName',  {  controller:  'ViewTagCtrl'  }) Tuesday, 13 August 13
  10. 10. Resolve for Rules Rejected Promises cause routes to fail. You can use this to make rules for the route like ACLs or prerequisites. Reject a promise to cause a route to fail. Using resolve to make rules means: •All resolution rules must pass before route succeeds and controller is instantiated. •Common ACLs (logged-in) specified in routes, not each controller or service •Can redirect the user to an appropriate page (also can do user-facing error) Tuesday, 13 August 13
  11. 11. when('/tag/:tagName',  {  resolve:  {    mustAuth  :  ['route',  function  (routeService)  {        return  routeService.mustAuth('/');}]}]}}); routeService.mustAuth  =  function  (redirectTo)  {  var  authDeferred,  p;  authDeferred  =  $q.defer();  p  =  userService.getCurrent();  p.then(function  (currentUser)  {    if  (angular.isUndefined(currentUser._id))  {      $location.url(redirectTo);      authDeferred.reject();    }  else  {  authDeferred.resolve(mustAuth);  }});  return  authDeferred.promise; }; Resolve for Rules Tuesday, 13 August 13
  12. 12. Resolve for Route-Specific UI Resolve can pass route-specific configuration to another service that affects the UI, like “show a promo”. Using resolve to control UI means: •Routes handle turning on and off UI elements •Route Controllers don’t need to worry about configuring site- wide UI elements like headers, promos, menus *There are other (possibly better) options for this, including having the UI controller listen to RouteChangeSuccess events. Tuesday, 13 August 13
  13. 13. when('/tag/:tagName',  {  resolve:  {    makeSparkPromo:  ['promo',      function  (promoService)  {        return  promoService.route('makeSparkPromo',  true); }]}}}); Resolve for Route-Specific UI Tuesday, 13 August 13
  14. 14. Testing Routes Midway Normally you can only test routing with E2E testing, slowly. Midway Testing (see Year Of Moo) is basically big unit testing. Use a router helper, which has routeDefined, getRoute. Midway testing routes means unittesting-like speed and E2E-like app setup. Tuesday, 13 August 13
  15. 15. ROUTER.when('tag',  '/tag/:tagName',  {  templateUrl:  '/partials/tag/tag-­‐detail.html',  controller:  'ViewTagCtrl' }); ROUTER.otherwise({  redirectTo  :  '/' }); ROUTER.install($routeProvider); Testing Routes Midway–Router Helper Tuesday, 13 August 13
  16. 16. before(function(done)  {  test  =  new  ngMidwayTester();  test.register('App',  done); }); it('should  have  a  route  to  a  tag  page',  function()  {        expect(  ROUTER.routeDefined('tags')).to.equal(true);  var  url  =  ROUTER.routePath('tags',  {tagName:  ‘cooking’);  expect(url).to.equal('/tag/cooking'); }); Testing Routes Midway–Testing Tuesday, 13 August 13
  17. 17. Recap The phonecat way (sticking everything under app) is unscalable. Angular’s route system is flexible and powerful, if you take advantage of resolve and promises. Using helper functions and midway testing means you can unit test more things. The less you rely on E2E testing, the better. Tuesday, 13 August 13
  18. 18. Other Good Ideas Others have addressed the same problems in a more reusable way. Angular UI router: use ‘state’ instead of routes. Seems good. Worth considering if you start fresh. Angular App: great example of well-designed app, but maybe hard to follow for a novice. Interesting crudRouteProvider implementation. Tuesday, 13 August 13