Upgrade to Pro — share decks privately, control downloads, hide ads and more …

An introduction to the Ember router

crofty
September 05, 2012
2.3k

An introduction to the Ember router

A talk from the London Ember.js User Group.

crofty

September 05, 2012
Tweet

Transcript

  1. Contacts = Ember.Application.create(); Contacts.ApplicationController = Ember.Controller.extend({}); Contacts.ApplicationView = Ember.View.extend({ templateName:

    'application' }) Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({}) }) Contacts.initialize();
  2. Contacts = Ember.Application.create(); Contacts.ApplicationController = Ember.Controller.extend({}); Contacts.ApplicationView = Ember.View.extend({ templateName:

    'application' }) Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({}) }) Contacts.initialize(); Create the application
  3. Contacts = Ember.Application.create(); Contacts.ApplicationController = Ember.Controller.extend({}); Contacts.ApplicationView = Ember.View.extend({ templateName:

    'application' }) Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({}) }) Contacts.initialize(); Define the application controller and view
  4. Controllers & Views • Each view is backed by a

    controller. • Controllers proxy the models and add client- side application state. • They need to be named xView and xController App.ApplicationView = Ember.View.extend({}) App.ApplicationController = Ember.Controller.extend({}) App.IndexView = Ember.View.extend({}) App.IndexController = Ember.Controller.extend({})
  5. Contacts = Ember.Application.create(); Contacts.ApplicationController = Ember.Controller.extend({}); Contacts.ApplicationView = Ember.View.extend({ templateName:

    'application' }) Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({}) }) Contacts.initialize();
  6. Naming conventions Assign classes to uppercase names App.Person = Ember.Object.extend({})

    App.PersonView = Ember.View.extend({}) App.PersonController = Ember.Controller.extend({})
  7. Naming conventions There’s an exception. Namespaces are always uppercase NamespaceOne

    = Ember.Namespace.extend({}) NamespaceTwo = Ember.Namespace.create()
  8. Contacts = Ember.Application.create(); Contacts.ApplicationController = Ember.Controller.extend({}); Contacts.ApplicationView = Ember.View.extend({ templateName:

    'application' }) Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({}) }) Contacts.initialize(); The application is a namespace
  9. Contacts = Ember.Application.create(); Contacts.ApplicationController = Ember.Controller.extend({}); Contacts.ApplicationView = Ember.View.extend({ templateName:

    'application' }) Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({}) }) Contacts.initialize(); Ember.Application = Ember.Namespace.extend({ //... })
  10. Contacts = Ember.Application.create(); Contacts.ApplicationController = Ember.Controller.extend({}); Contacts.ApplicationView = Ember.View.extend({ templateName:

    'application' }) Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({}) }) Contacts.initialize(); so even though it is an instance, it is uppercased
  11. Contacts = Ember.Application.create(); Contacts.ApplicationController = Ember.Controller.extend({}); Contacts.ApplicationView = Ember.View.extend({ templateName:

    'application' }) Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({}) }) Contacts.initialize(); Everything else uses extend.
  12. Contacts = Ember.Application.create(); Contacts.ApplicationController = Ember.Controller.extend({}); Contacts.ApplicationView = Ember.View.extend({ templateName:

    'application' }) Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({}) }) Contacts.initialize(); <script type="text/x-handlebars" data-template-name="application"> <h1>Contacts</h1> </script>
  13. Contacts = Ember.Application.create(); Contacts.ApplicationController = Ember.Controller.extend({}); Contacts.ApplicationView = Ember.View.extend({ templateName:

    'application' }) Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({}) }) Contacts.initialize(); Subclass Ember.Router. This must be assigned to a property named Router
  14. Contacts = Ember.Application.create(); Contacts.ApplicationController = Ember.Controller.extend({}); Contacts.ApplicationView = Ember.View.extend({ templateName:

    'application' }) Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({}) }) Contacts.initialize(); Options are: hash, history (pushState), none
  15. Contacts = Ember.Application.create(); Contacts.ApplicationController = Ember.Controller.extend({}); Contacts.ApplicationView = Ember.View.extend({ templateName:

    'application' }) Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({}) }) Contacts.initialize(); Define a route named ‘root’. This will act as a container for all other states. The router will transition to this state upon initialisation
  16. Contacts = Ember.Application.create(); Contacts.ApplicationController = Ember.Controller.extend({}); Contacts.ApplicationView = Ember.View.extend({ templateName:

    'application' }) Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({}) }) Contacts.initialize(); Ember.Router = Ember.StateManager.extend({ initialState: 'root', ... })
  17. Contacts = Ember.Application.create(); Contacts.ApplicationController = Ember.Controller.extend({}); Contacts.ApplicationView = Ember.View.extend({ templateName:

    'application' }) Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({}) }) Contacts.initialize(); Initialise the application
  18. Contacts = Ember.Application.create(); Contacts.ApplicationController = Ember.Controller.extend({}); Contacts.ApplicationView = Ember.View.extend({ templateName:

    'application' }) Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({}) }) Contacts.initialize(); • Initialise an instance of the router • Look through the namespace for anything named xController and initialise it as a property on the router
  19. Contacts.get('router').toString() // <Contacts.Router:ember286> Contacts.get('router.applicationController').toString() // <Contacts.ApplicationController:ember287> Post initialisation An instance

    of the router has been created and is available on the application namespace An instance of the ApplicationController has been created and is available on the router instance
  20. Contacts = Ember.Application.create(); Contacts.ApplicationController = Ember.Controller.extend({}); Contacts.ApplicationView = Ember.View.extend({ templateName:

    'application' }) Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({}) }) Contacts.initialize(); Putting it all together...
  21. Contacts.IndexController = Ember.ArrayController.extend({}); Contacts.IndexView = Ember.View.extend({ templateName: 'index' }) <script

    type="text/x-handlebars" data-template-name="index"> <h2>Index</h2> </script> Add the controller, view and template
  22. Add an outlet to the application template <script type="text/x-handlebars" data-template-name="application">

    <h1>Contacts</h1> {{outlet}} </script> Outlets are things that we ‘plug’ views into.
  23. Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({ index: Ember.Route.extend({ route:

    '/', connectOutlets: function(router){ router.get('applicationController').connectOutlet('index') } }) }) }) Add the new state
  24. Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({ index: Ember.Route.extend({ route:

    '/', connectOutlets: function(router){ router.get('applicationController').connectOutlet('index') } }) }) }) Add the new state Leaf states have a route property which is set to a url. When the browser url matches this url, the application will transition into this state
  25. Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({ index: Ember.Route.extend({ route:

    '/', connectOutlets: function(router){ router.get('applicationController').connectOutlet('index') } }) }) }) Add the new state When the application first loads, the browser will have a url of ‘/‘ and the router will transition into this state
  26. Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({ index: Ember.Route.extend({ route:

    '/', connectOutlets: function(router){ router.get('applicationController').connectOutlet('index') } }) }) }) Add the new state connectOutlets is a callback that gets triggered when the application transition into the state. We use the callback to plug the views into any outlets that we’ve defined in the templates.
  27. Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({ index: Ember.Route.extend({ route:

    '/', connectOutlets: function(router){ router.get('applicationController').connectOutlet('index') } }) }) }) Add the new state We grab our application controller instance from the router...
  28. Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({ index: Ember.Route.extend({ route:

    '/', connectOutlets: function(router){ router.get('applicationController').connectOutlet('index') } }) }) }) Add the new state and call connectOutlet on it, passing ‘index’ as an argument
  29. Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({ index: Ember.Route.extend({ route:

    '/', connectOutlets: function(router){ router.get('applicationController').connectOutlet('index') } }) }) }) Add the new state ‘index’ means that an instance of IndexView will be created and plugged into the outlet. The view will be backed by the instance of IndexController that was initialised for us.
  30. Contacts = Ember.Application.create(); Contacts.ApplicationController = Ember.Controller.extend({}); Contacts.ApplicationView = Ember.View.extend({ templateName:

    'application' }) Contacts.IndexController = Ember.ArrayController.extend({}); Contacts.IndexView = Ember.View.extend({ templateName: 'index' }) Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({ index: Ember.Route.extend({ route: '/', connectOutlets: function(router){ router.get('applicationController').connectOutlet('index') } }) }) }) Contacts.initialize(); Putting it all together...
  31. Controller, View & template Contacts.ShowController = Ember.ObjectController.extend({}); Contacts.ShowView = Ember.View.extend({

    templateName: 'show' }) <script type="text/x-handlebars" data-template-name="show"> <h2>Show</h2> </script>
  32. Route Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({ index: Ember.Route.extend({

    route: '/', connectOutlets: function(router){ router.get('applicationController').connectOutlet('index') } }), show: Ember.Route.extend({ route: '/show', connectOutlets: function(router){ router.get('applicationController').connectOutlet('show') } }) }) })
  33. Route Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({ index: Ember.Route.extend({

    route: '/', connectOutlets: function(router){ router.get('applicationController').connectOutlet('index') } }), show: Ember.Route.extend({ route: '/show', connectOutlets: function(router){ router.get('applicationController').connectOutlet('show') } }) }) })
  34. Put some links in the templates <script type="text/x-handlebars" data-template-name="index"> <h2>Index

    page</h2> <a {{action goToShow href=true}}>Go to show</a> </script> <script type="text/x-handlebars" data-template-name="show"> <h2>Show page</h2> <a {{action goToIndex href=true}}>Go to index</a> </script>
  35. Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({ goToIndex: Ember.Route.transitionTo('index'), goToShow:

    Ember.Route.transitionTo('show'), index: Ember.Route.extend({ route: '/', connectOutlets: function(router){ router.get('applicationController').connectOutlet('index') } }), show: Ember.Route.extend({ route: '/show', connectOutlets: function(router){ router.get('applicationController').connectOutlet('show') } }) }) }) Add the transition methods
  36. • Click link • goToShow action is called on the

    router • Application transitions into show state
  37. Data Ember.Object.create({ name: 'James Croft', city: 'London', //... }) Contacts

    are simple Ember Objects created with data pulled from the meetup.com API
  38. Data API Contacts.Data.all() // returns an array of all the

    contacts Contacts.Data.findByName('james') // returns the contact with name james
  39. Change our index template to list all the contacts <script

    type="text/x-handlebars" data-template-name="index"> <ul> {{#each contact in controller}} <li><a {{action showContact contact href=true}}>{{contact.name}}</a></li> {{/each}} </ul> </script>
  40. Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({ goToIndex: Ember.Route.transitionTo('index'), goToShow:

    Ember.Route.transitionTo('show'), index: Ember.Route.extend({ route: '/', connectOutlets: function(router){ router.get('applicationController').connectOutlet({ name: 'index', context: Contacts.Data.all() }); } }), show: Ember.Route.extend({ route: '/show', connectOutlets: function(router){ router.get('applicationController').connectOutlet('show'); } }) }) }) Pass the data to the controller
  41. <script type="text/x-handlebars" data-template-name="index"> <ul> {{#each contact in controller}} <li><a {{action

    showContact contact href=true}}>{{contact.name}}</a></li> {{/each}} </ul> </script>
  42. Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({ goToIndex: Ember.Route.transitionTo('index'), showContact:

    Ember.Route.transitionTo('show'), index: Ember.Route.extend({ route: '/', connectOutlets: function(router){ router.get('applicationController').connectOutlet({ name: 'index', context: Content.Data.all() }) } }), show: Ember.Route.extend({ route: '/show/:name', connectOutlets: function(router){ router.get('applicationController').connectOutlet('show') } }) }) })
  43. Change our show template to list the contact info <script

    type="text/x-handlebars" data-template-name="show"> <h2>{{name}}</h2> <img {{bindAttr src="photo"}}/> <dl> <dt>Bio</dt> <dd>{{bio}}</dd> <dt>City</dt> <dd>{{city}}</dd> <dt>Topics</dt> <dd> <ul> {{#each topic in topics}} <li>{{topic}}</li> {{/each}} </ul> </dd> </dl> </script>
  44. Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({ goToIndex: Ember.Route.transitionTo('index'), showContact:

    Ember.Route.transitionTo('show'), index: Ember.Route.extend({ route: '/', connectOutlets: function(router){ router.get('applicationController').connectOutlet({ name: 'index', context: Content.Data.all() }) } }), show: Ember.Route.extend({ route: '/show/:name', connectOutlets: function(router,context){ router.get('applicationController').connectOutlet({ name: 'show', context: context }) } }) }) }) Pass the data to the controller
  45. • Entering the application at a particular URL doesn’t give

    us access to the previously loaded data. • We need something to convert the params hash from the matched url into an object url http://localhost:3000/#/show/James Croft matches route /show/:name with params {name: 'James Croft'} We need to use our params to find our contact.
  46. Contacts.Router = Ember.Router.extend({ //... show: Ember.Route.extend({ route: '/show/:name', connectOutlets: function(router,context){

    router.get('applicationController').connectOutlet({ name: 'show', context: context }) }, deserialize: function(router, urlParams){ return Contacts.Data.findByName(urlParams.name); } }) }) })
  47. Contacts.ApplicationController = Ember.Controller.extend({}); Contacts.ApplicationView = Ember.View.extend({ templateName: 'application' }) Contacts.IndexController

    = Ember.ArrayController.extend({}); Contacts.IndexView = Ember.View.extend({ templateName: 'index' }) Contacts.ShowController = Ember.ObjectController.extend({}); Contacts.ShowView = Ember.View.extend({ templateName: 'show' }) Contacts.Router = Ember.Router.extend({ location: 'hash', root: Ember.Route.extend({ goToIndex: Ember.Route.transitionTo('index'), showContact: Ember.Route.transitionTo('show'), index: Ember.Route.extend({ route: '/', connectOutlets: function(router){ router.get('applicationController').connectOutlet({ name: 'index', context: Contacts.Data.all() }); } }), show: Ember.Route.extend({ route: '/show/:name', connectOutlets: function(router, context){ router.get('applicationController').connectOutlet({ name: 'show', context: context }) }, deserialize: function(router, urlParams){ return Contacts.Data.findByName(urlParams.name); } }) }) }) Contacts.initialize();
  48. In our view we have: {{ outlet }} This is

    the same as: {{view Ember.ContainerView currentViewBinding=”controller.view”}}
  49. So we can replace: connectOutlets: function(router){ router.get('applicationController').connectOutlet({ name: 'index', context:

    Contacts.Data.all() }); } With: connectOutlets: function(router){ controller = router.get('indexController') controller.set('content', Contacts.Data.all()) view = Contacts.IndexView.create({controller: controller}) router.set('applicationController.view', view) } And it all still works.
  50. An outlet just refers to an instance of a view

    on the controller. You can set these up manually if you need it.