Share
Embed
An introduction to the Ember router
A talk from the London Ember.js User Group.
crofty
September 05, 2012
Transcript
73 slides
-
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(); -
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 -
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 -
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({}) -
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(); -
Naming conventions Assign classes to uppercase names App.Person = Ember.Object.extend({})
App.PersonView = Ember.View.extend({}) App.PersonController = Ember.Controller.extend({}) -
Naming conventions Assign instances to lowercase names App.john = App.Person.create()
App.personController = Ember.Controller.create() -
Naming conventions There’s an exception. Namespaces are always uppercase NamespaceOne
= Ember.Namespace.extend({}) NamespaceTwo = Ember.Namespace.create() -
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 -
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({ //... }) -
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 -
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. -
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> -
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 -
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 -
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 -
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', ... }) -
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 -
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 -
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 -
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... -
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 -
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. -
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 -
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 -
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 -
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. -
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... -
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 -
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. -
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... -
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> -
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') } }) }) }) -
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') } }) }) }) -
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> -
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 -
• Click link • goToShow action is called on the
router • Application transitions into show state -
Data Ember.Object.create({ name: 'James Croft', city: 'London', //... }) Contacts
are simple Ember Objects created with data pulled from the meetup.com API -
Data API Contacts.Data.all() // returns an array of all the
contacts Contacts.Data.findByName('james') // returns the contact with name james -
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> -
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 -
<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> -
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') } }) }) }) -
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> -
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 -
• 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. -
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); } }) }) }) -
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(); -
In our view we have: {{ outlet }} This is
the same as: {{view Ember.ContainerView currentViewBinding=”controller.view”}} -
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. -
An outlet just refers to an instance of a view
on the controller. You can set these up manually if you need it.