Ember.js Workshop
Greg Malcolm github.com/gregmalcolm @gregmalcolm
Preparation
See README.md for details of things to install
Starting project
We're going to be changing the following files:
- File: index.html - Handlebar templates
Ember javascript code:
- js/app.js
- js/models.js
- js/fixtures.js
- js/controllers.js
- js/routes.js
Starting Project
We need a simple http server for Ember to work correctly.
Start the server by running this:
$ bin/server
... or...
C:\ember-beginner-workshop> bin\server.cmd
Then go look at the project in localhost:3000 in Chrome
Starting Project

Starting Project
For reference consult the Ember Guides:

Objective 1
List stories in the Index Route
Customize the Index Route to show stories instead of colors

Objective 1
List stories in the Index Route
Tasks
- Personalize the page design
- Change Application Template to show a simple navbar
- Change the Index Route to use story names as the Model
- Show stories in the Index Template
List stories in the Index Route
Task - Personalize the page design
Personalize the title
... <title>Choose Your Own Adventure</title> ...
File: index.html
List stories in the Index Route
Task - Personalize the page design
Change out the Application Template for one containing a Bootstrap navbar.
Replace this:
<script type="text/x-handlebars"> <h2>Welcome to Ember.js</h2> </script>
File: index.html
List stories in the Index Route
Task - Personalize the page design
Change out the Application Template for one containing a Bootstrap navbar.
... With this:
<!--== Templates ==--> <script type="text/x-handlebars">
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation"> <div class="navbar-header"> <a class="navbar-brand" href="#">Choose your own Adventure</a> </div> </nav> {{outlet}}
</script>
File: index.html
List stories in the Index Route
Task - Change the Index Route to use story names as the Model
Change the Index Route in js/routes.js to look like this:
App.IndexRoute = Ember.Route.extend({
model: function() { return ['Temple Quest', 'Adventure!', 'Zombies', 'Death Race!']; } });
File: js/routes.js
List stories in the Index Route
Task - Show stories in the Index Template
In index.html, find the Index Template (it contains data-template-name="index").
Replace it with this
<!--== Templates with routes ==--> <!-- / --> <script type="text/x-handlebars" data-template-name="index">
<h3>Stories:</h3> <ul> {{#each title in content}} <li>{{title}}</li> {{/each}} </ul>
</script>
File: index.html
List stories in the Index Route
You should now have this:

Problems? Try running this to compare against a working version:
$ git diff -w fbe2d index.html js
Objective 2
Use ember-data Models and Fixtures for data
Create a new Ember Route called stories that lists the available stories
They will be viewable from http://localhost:3000/#/stories

Using ember-data Models and Fixtures for data
Tasks
- Create a new Route called 'stories'
- Set the Stories Route model to return 'story' Models
- Make the default Index Route reroute to Stories Route
Using ember-data Models and Fixtures for data
Task - Create a new Route called 'stories'
Configure the Router to support #/stories.
Replace the Router configuration with this:
App.Router.map(function() { this.resource('stories', function() {}); });
File: js/routes.js
Using ember-data Models and Fixtures for data
Task - Create a new Route called 'stories'
Get rid of the old Index Route implementation:
App.IndexRoute = Ember.Route.extend({ model: function() { return ['Temple Quest', 'Adventure!', 'Zombies', 'Death Race!']; } });
File: js/routes.js
Using ember-data Models and Fixtures for data
Task - Set the Stories Route model to return 'story' Models
Uncomment the Fixture Adapter and Story Model in js/models.js. Leave first_section attribute commented out:
App.ApplicationAdapter = DS.FixtureAdapter.extend(); App.Story = DS.Model.extend({ title: DS.attr('string'), //first_section: DS.belongsTo('section') });
File: js/models.js
Using ember-data Models and Fixtures for data
Task - Set the Stories Route model to return 'story' Models
Uncomment the Story Fixture in js/fixtures.js
App.Story.FIXTURES = [ { id: 100, title: 'Temple Quest', first_section: 2000 }, ... ];
File: js/fixtures.js
Using ember-data Models and Fixtures for data
Task - Set the Stories Route model to return 'story' Models
Implement the StoriesIndex Route
App.StoriesIndexRoute = Ember.Route.extend({ model: function() { return this.get("store").findAll("story"); } });
File: js/routes.js
Using ember-data Models and Fixtures for data
Task - Set the Stories Route model to return 'story' Models
Remove the Index Template from index.html
<!-- / --> <script type="text/x-handlebars" data-template-name="index"> <h3>Stories:</h3> <ul> {{#each title in content}} <li>{{title}}</li> {{/each}} </ul> </script>
File: index.html
Using ember-data Models and Fixtures for data
Task - Set the Stories Route model to return 'story' Models
Insert the replacement stories/index page
<!-- /stories --> <script type="text/x-handlebars" data-template-name="stories/index">
<h3>Stories:</h3> <ul> {{#each}} <li>{{title}}</li> {{/each}} </ul>
</script>
File: index.html
Using ember-data Models and Fixtures for data
Task - Make the default Index Route reroute to Stories Route
Implement a new Index Route in js/routes.js
App.IndexRoute = Ember.Route.extend({ redirect: function(params) { this.transitionTo("stories"); } });
File: js/routes.js
Using ember-data Models and Fixtures for data
Done!

Problems? Compare with working version:
$ git diff -w 424ae index.html js
Objective 3
Create a Story Viewer page
Create a new Ember Route called story for reading a story

Create a Story Viewer page
Tasks
- Create a 'show' Route
- Create a 'show' Template
- Configure the Story Show Route to load the Story Model
Create a Story Viewer page
Task - Create a 'show' Route
Update the Router object to include the 'show' entry:
App.Router.map(function() { this.resource('stories', function() { this.route('show', { path: ':story_id'}); }); });
File: js/routes.js
Create a Story Viewer page
Task - Create a 'show' Route
In the stories/index Template replace the title binding with a link-to helper:
<!--== Templates with routes ==--> <!-- /stories --> <script type="text/x-handlebars" data-template-name="stories/index">
<h3>Stories:</h3> <ul> {{#each story in content}} <li>{{#link-to 'stories.show' story}} {{story.title}} {{/link-to}}</li> {{/each}} </ul>
</script>
File: index.html
Create a Story Viewer page
Task - Create a 'show' Template
Insert the following under the stories index template:
<!-- /stories/:story_id --> <script type="text/x-handlebars" data-template-name="stories/show"> <h3>{{title}}</h3> </script>
File: index.html
Create a Story Viewer page
Task - Configure the Story Show Route to load the Story Model
Insert the following StoriesShow Route definition to js/routes.js:
App.StoriesShowRoute = Ember.Route.extend({ model: function(params) { return this.get("store").find('story', params.story_id); } });
File: js/routes.js
Create a Story Viewer page
Fini!

Problems? Compare with working version:
$ git diff -w 8745e index.html js
Objective 4
Story Editor
Editing would be nice!

Story Editor
Tasks
- Create an edit route
- Implement Save
- Implement Cancel
- Go to the Show page after saving
- Update links in other pages
Story Editor
Task - Create an edit route
Add an 'edit' Route to the Router:
App.Router.map(function() { this.resource('stories', function() { this.route('show', { path: ':story_id'}); this.route('edit', { path: ':story_id/edit'}); }); });
File: js/routes.js
Story Editor
Task - Create an edit route
We're going to make the Show and Edit Route reuse the same base class.
First remove the old Show Route...
App.StoriesShowRoute = Ember.Route.extend({ model: function(params) { return this.get("store").find('story', params.story_id); } });
File: js/routes.js
Story Editor
Task - Create an edit route
... and replace it with this:
App.StoriesBaseRoute = Ember.Route.extend({ model: function(params) { return this.get("store").find('story', params.story_id); } }); App.StoriesShowRoute = App.StoriesBaseRoute.extend(); App.StoriesEditRoute = App.StoriesBaseRoute.extend();
File: js/routes.js
Story Editor
Task - Create an edit route
We're also going to need an edit template
<!-- /stories/:story_id/edit --> <script type="text/x-handlebars" data-template-name="stories/edit"> <button {{action save}} class="btn btn-success">Save</button> <button {{action cancel}} class="btn btn-danger">Cancel</button> <h3>Edit {{title}}</h3> <fieldset> <label>Title</label> {{input value=title size="80"}} </fieldset> </script>
File: index.html
Story Editor
Task - Implement Save
To make the save button work we're going to implement our first Controller action
App.StoriesEditController = Ember.ObjectController.extend({ actions: { save: function() { this.get("content").save(); } }, });
File: js/controllers.js
Story Editor
Task - Implement Cancel
The same controller also needs a cancel action. Add it:
App.StoriesEditController = Ember.ObjectController.extend({ actions: { save: function() { this.get("content").save(); }, cancel: function() { this.get("content").rollback(); this.transitionToRoute("stories.show"); } }, });
File: js/controllers.js
Story Editor
Task - Go to the Show page after saving
Add an after save callback:
App.StoriesEditController = Ember.ObjectController.extend({ actions: { save: function() { this.get("content").save().then( this.didSave.bind(this) ); }, ... }, didSave: function(story) { this.transitionToRoute("stories.show"); } });
File: js/controllers.js
Story Editor
Task - Update links in other pages
Add a link to the Edit Route from the Show Route:
<!-- /stories/:story_id --> <script type="text/x-handlebars" data-template-name="stories/show"> {{#link-to 'stories.edit' content}} <button class="btn btn-primary">Edit</button> {{/link-to}} <h3>{{title}}</h3> </script>
File: index.html
Story Editor
Task - Update links in other pages
Replace the story links with a call to a new Partial Template in the Stories Template:
<!--== Templates with routes ==--> <!-- /stories --> <script type="text/x-handlebars" data-template-name="stories/index"> <h3>Stories:</h3> {{partial "stories/storiesList"}} </script>
File: index.html
Story Editor
Task - Update links in other pages
Insert the Partial just after the other Template declarations
<!--== Partials ==--> <script type="text/x-handlebars" data-template-name="stories/_storiesList">
<ul> {{#each story in content}} <li> {{#link-to 'stories.edit' story}} <button class="btn btn-primary btn-xs">Edit</button> {{/link-to}} {{#link-to 'stories.show' story}}{{story.title}}{{/link-to}} </li> {{/each}} </ul>
</script>
File: index.html
Story Editor
Yay, did it!

Problems? Compare with working version:
$ git diff -w 04824 index.html js
Objective 5
Creating And Destroying Stories
I want to create my own stories!

Creating and Destroying Stories
Tasks
- Create Story
- Delete Story
Creating and Destroying Stories
Task - Create Story
Add a 'new' Route to the Router:
App.Router.map(function() { this.resource('stories', function() { this.route('new'); this.route('show', { path: ':story_id'}); this.route('edit', { path: ':story_id/edit'}); }); });
File: js/routes.js
Creating and Destroying Stories
Task - Create Story
Implement the StoriesNew Route:
App.StoriesNewRoute = Ember.Route.extend({ model: function(params) { return this.get("store").createRecord("story"); } });
File: js/routes.js
Creating and Destroying Stories
Task - Create Story
Create a new Template for Route stories/new:
<!-- /stories/:story_id --> <script type="text/x-handlebars" data-template-name="stories/new"> <h3>New Story</h3> <fieldset> <label>Title</label> {{input value=title size="80"}} </fieldset> <br> <button {{action create}} class="btn btn-success">Create</button> <button {{action cancel}} class="btn btn-danger">Cancel</button> </script>
File: index.html
Creating and Destroying Stories
Task - Create Story
Add a button to the stories/index Template for creating stories:
<!-- /stories --> <script type="text/x-handlebars" data-template-name="stories/index">
<h3>Stories:</h3> {{#link-to 'stories.new'}} <button class = "btn btn-success">New Story</button> {{/link-to}} <br> {{partial "stories/storiesList"}}
</script>
File: index.html
Creating and Destroying Stories
Task - Create Story
Insert a new base class for editing and creating for 'cancel' actions:
App.StoriesModifyController = Ember.ObjectController.extend({ actions: { cancel: function() { this.get("content").rollback(); this.transitionToRoute("stories.index"); } }, });
File: js/controllers.js
Creating and Destroying Stories
Task - Create Story
In StorieEditController change the base class and remove the 'cancel' action:
App.StoriesEditController = App.StoriesModifyController.extend({ actions: { save: function() { this.get("content").save().then( this.didSave.bind(this) ); } }, didSave: function(story) { this.transitionToRoute("stories.show"); } });
File: js/controllers.js
Creating and Destroying Stories
Task - Create Story
Insert a new controller for new stories, also using the new base class:
App.StoriesNewController = App.StoriesModifyController.extend({ actions: { create: function() { this.get("content").save().then( this.didCreate.bind(this) ); } }, didCreate: function(story) { this.transitionToRoute("stories.edit", story); } });
File: js/controllers.js
Creating and Destroying Stories
Task - Delete Story
Add a button to the stories/index Template for deleting stories:
<!--== Partials ==--> <script type="text/x-handlebars" data-template-name="stories/_storiesList">
<ul> {{#each story in content}} <li> {{#link-to 'stories.edit' story}} <button class="btn btn-primary btn-xs">Edit</button> {{/link-to}} <button {{action "destroy" story}} class="btn btn-danger btn-xs">Delete</button> {{#link-to 'stories.show' story}}{{story.title}}{{/link-to}} </li> {{/each}} </ul>
</script>
File: index.html
Creating and Destroying Stories
Task - Delete Story
Implement StoriesIndex Controller to handle 'delete' action:
App.StoriesIndexController = Ember.ArrayController.extend({ actions: { destroy: function(story) { story.deleteRecord(); story.save(); } } });
File: js/controllers.js
Story Editor
CRUDtastic!

Problems? Compare with working version:
$ git diff -w 22cc2 index.html js
Objective 6
Showing Story Sections
Might be real nice if our stories had some content!

Showing Story Sections
Tasks
- Show first story section
- Display section choices
- Render Section Route
Showing Story Sections
Task - Show first story section
Uncomment first_section attribute of Story Model:
App.Story = DS.Model.extend({ title: DS.attr('string'), first_section: DS.belongsTo('section') });
File: js/models.js
Showing Story Sections
Task - Show first story section
Uncomment Section Model (except choices attribute):
App.Section = DS.Model.extend({ synopsis: DS.attr('string'), text: DS.attr('string'), story: DS.belongsTo('story'), // choices: DS.hasMany('choice', {async: true, inverse: 'section'}) });
File: js/models.js
Showing Story Sections
Task - Show first story section
Uncomment Section Fixtures:
App.Section.FIXTURES = [ { id: 2000, synopsis: "Intro", text: "It is a lovely summers day. While lying on your back watching " + "clouds you notice a small dark dot in the sky above you.", story: 100, choices: [3000, 3001, 3002] }, ... }];
File: js/fixtures.js
Showing Story Sections
Task - Show first story section
Add a new Template (section-show):
<!--== Routeless Templates (used through 'render' helper)--> <!-- section-show --> <script type="text/x-handlebars" data-template-name="section-show"> {{content.text}} </script>
File: js/fixtures.js
Showing Story Sections
Task - Show first story section
Add Render Helper to stories/show Template:
<!-- /stories/:story_id --> <script type="text/x-handlebars" data-template-name="stories/show"> {{#link-to 'stories.edit' content}} <button class="btn btn-primary">Edit</button> {{/link-to}} <h3>{{title}}</h3> {{render section-show first_section}} </script>
File: js/fixtures.js
Showing Story Sections
Task - Display section choices
Uncomment Section Model's choices attribute:
App.Section = DS.Model.extend({ synopsis: DS.attr('string'), text: DS.attr('string'), story: DS.belongsTo('story'), choices: DS.hasMany('choice', {async: true, inverse: 'section'}) });
File: js/models.js
Showing Story Sections
Task - Display section choices
Uncomment Choice Model:
App.Choice = DS.Model.extend({ wording: DS.attr('string'), section: DS.belongsTo('section'), goes_to: DS.belongsTo('section') });
File: js/models.js
Showing Story Sections
Task - Display section choices
Uncomment Choice Fixtures:
App.Choice.FIXTURES = [ { id: 3000, wording: "Go back to sleep", goes_to: 2010 }, ... }];
File: js/fixtures.js
Showing Story Sections
Task - Display section choices
Replace Show Route with a Resource containing Sections route:
App.Router.map(function() { this.resource('stories', function() { this.route('new'); this.resource('stories.show', { path: ':story_id'}, function() { this.route('sections', { path: '/sections/:section_id' }); }); this.route('edit', { path: ':story_id/edit'}); }); });
File: js/routes.js
Showing Story Sections
Task - Display section choices
Implement new StoriesShowIndex Route:
App.StoriesShowIndexRoute = Ember.Route.extend({ model: function(params) { model = this.modelFor("stories.show"); return model; } });
File: js/routes.js
Showing Story Sections
Task - Display section choices
Create a new stories/show/index Template:
<!-- /stories/:story_id/<index> --> <script type="text/x-handlebars" data-template-name="stories/show/index"> {{render section-show first_section}} </script>
File: index.html
Showing Story Sections
Task - Display section choices
Inside stories/show Template replace the {{render}} declaration with {{outlet}}:
<!-- /stories/:story_id --> <script type="text/x-handlebars" data-template-name="stories/show"> {{#link-to 'stories.edit' content}} <button class="btn btn-primary">Edit</button> {{/link-to}} <h3>{{title}}</h3> {{outlet}} </script>
File: index.html
Showing Story Sections
Task - Display section choices
Change section-show Template to show choices:
<script type="text/x-handlebars" data-template-name="section-show">
{{content.text}} <br> <br> {{#if content.choices}} <p>What do you want to do?<p> <ul> {{#each content.choices}} <li>{{#link-to 'stories.show.sections' goes_to}} {{wording}} {{/link-to}}</li> {{/each}} </ul> {{else}} <p><strong>The End</strong></p> {{/if}}
</script>
File: index.html
Showing Story Sections
Task - Render Section Route
Insert a template for stories/show/sections:
<!-- /stories/:story_id/sections/:section_id --> <script type="text/x-handlebars" data-template-name="stories/show/sections"> {{render section-show content}} </script>
File: index.html
Showing Story Sections
It's playable!

Problems? Compare with working version:
$ git diff -w 4b671 index.html js
Objective 7
Editing Story Sections
Great, but how do we make changes to sections?

Editing the first Section
Tasks
- Edit routing for story sections
- Apply choices
- Removable choices
- Adding choices
Editing the first Section
Task - Edit routing for story sections
Turn 'edit' route into a resource containing a section route:
App.Router.map(function() { this.resource('stories', function(){ this.route('new'); this.resource('stories.show', {path: ':story_id'}, function(){ this.route('sections', {path: '/sections/:section_id' }); }); this.resource('stories.edit',{path: ':story_id/edit'}, function(){ this.route('sections', {path: '/sections/:section_id' }); }); }); });
File: js/routes.js
Editing the first Section
Task - Edit routing for story sections
Insert a new Edit Index Route
App.StoriesEditIndexRoute = Ember.Route.extend({ model: function(params) { return this.modelFor("stories.edit"); } });
File: js/routes.js
Editing the first Section
Task - Edit routing for story sections
Insert a new Template for stories/edit/index
<!-- /stories/:story_id/edit<index> --> <script type="text/x-handlebars" data-template-name="stories/edit/index"> {{render section-edit first_section}} </script>
File: index.html
Editing the first Section
Task - Edit routing for story sections
Replace the {{render}} helper with {{outlet}} in stories/edit Template
<!-- /stories/:story_id/edit --> <script type="text/x-handlebars" data-template-name="stories/edit"> <button {{action save}} class="btn btn-success">Save</button> <button {{action cancel}} class="btn btn-danger">Cancel</button> <h3>Edit {{title}}</h3> <fieldset> <label>Title</label> {{input value=title size="80"}} </fieldset> {{outlet}} </script>
File: index.html
Editing the first Section
Task - Edit routing for story sections
Insert a new Template for stories/edit/sections
<!-- /stories/:story_id/edit/sections/:section_id --> <script type="text/x-handlebars" data-template-name="stories/edit/sections"> {{render section-edit content}} </script>
File: index.html
Editing the first Section
Task - Apply choices
Add {{render choices-edit content.choices}} to the bottom area section-edit Template
<!-- section-edit --> <script type="text/x-handlebars" data-template-name="section-edit"> <h4>Section: {{content.synopsis}}</h4> <div> <fieldset> <label>Short synopsis:</label> {{input value=content.synopsis}} </fieldset> <fieldset> <label>Content:</label><br> {{textarea value=content.text cols=100 rows=6}} </fieldset> {{render choices-edit content.choices}} </div> </script>
File: index.html
Editing the first Section
Task - Apply choices
Insert a Template called choices-edit
<!-- choices-edit --> <script type="text/x-handlebars" data-template-name="choices-edit">
<h4>Choices:</h4> <ul> {{#each choice in content}} <li> {{#link-to 'stories.edit.sections' choice.goes_to}} {{choice.wording}} {{/link-to}} </li> {{/each}} </ul>
</script>
File: index.html
Editing the first Section
Task - Removable choices
Add a Remove button to the choices-edit
<!-- choices-edit --> <script type="text/x-handlebars" data-template-name="choices-edit">
<h4>Choices:</h4> <ul> {{#each choice in content}} <li> <button {{action "remove" choice}} class="btn btn-danger btn-xs">Remove</button> {{#link-to 'stories.edit.sections' choice.goes_to}} {{choice.wording}} {{/link-to}} </li> {{/each}} </ul>
</script>
File: index.html
Editing the first Section
Task - Removable choices
Insert a controller for choices-edit template to handle deletions
App.ChoicesEditController = Ember.ArrayController.extend({ actions: { remove: function(choice) { choice.deleteRecord(); choice.save().then(this.didRemove.bind(this), this.didRejectRemove.bind(this)); } }, didRemove: function(choice) { choices = this.get("content") choices.removeObject(choice) }, didRejectRemove: function(reason) { console.error("Failed to remove item", reason); } });
File: js/controllers.js
Editing the first Section
Task - Adding choices
Place a {{render "choice-add"}} declaration at the bottom of choices-add Template
<!-- choices-edit --> <script type="text/x-handlebars" data-template-name="choices-edit">
<h4>Choices:</h4> <ul> {{#each choice in content}} <li><button {{action "remove" choice}} class="btn btn-danger btn-xs">Remove</button> {{#link-to 'stories.edit.sections' choice.goes_to}} {{choice.wording}} {{/link-to}} </li> {{/each}} </ul> {{render "choice-add"}}
</script>
File: index.html
Editing the first Section
Task - Adding choices
Add a new choices-edit Template
<!-- choices-add --> <script type="text/x-handlebars" data-template-name="choice-add"> <label>New choice</label><br> {{input value=wording placeholder="Add an extra choice here" size="60"}} <button {{action "add"}} class="btn btn-success btn-xs">Add</button> </script>
File: index.html
Editing the first Section
Task - Adding choices
Add a new Choice Add controller (Part 1/2)
App.ChoiceAddController = Ember.ObjectController.extend({ needs: "section-edit", content: function() { return this.createChoiceObject(); }.property(''), actions: { add: function() { choice = this.get("content"); choice.set("goes_to", this.newSection()); this.parentController().pushObject(choice); choice.save().then(this.didAdd.bind(this)); } }, ...
File: js/controllers.js
Editing the first Section
Task - Adding choices
Add a new Choice Add controller (Part 2/2)
... didAdd: function() { this.set("content", this.createChoiceObject()); }, createChoiceObject: function() { return this.get("store").createRecord('choice'); }, parentController: function() { return this.get("controllers.section-edit.choices"); }, newSection: function() { return this.get("store").createRecord('section'); } });
File: js/controllers.js
Showing Story Sections
Almost there!

Problems? Compare with working version:
$ git diff -w 6d9df index.html js
Objective 8
Finishing Touches

Finishing Touches
Tasks
- Make editing work from all Sections
- Make 'New Story' work with choices
Finishing Touches
Task - Make editing work from all Sections
Refactor: Move StoriesShowIndex Route content into StoriesShowBaseRoute and inherit from it:
App.StoriesShowBaseRoute = Ember.Route.extend({ model: function(params) { return this.modelFor("stories.show"); } }); App.StoriesShowIndexRoute = App.StoriesShowBaseRoute.extend({});
File: js/routes.js
Finishing Touches
Task - Make editing work from all Sections
Implement Route StoriesShowSectionsRoute, extending functionality of StoriesShowBaseRoute:
App.StoriesShowSectionsRoute = App.StoriesShowBaseRoute.extend({ model: function(params) { this._super(params); return this.store.find('section', params.section_id); } });
File: js/routes.js
Finishing Touches
Task - Make editing work from all Sections
Change stories/show edit button to use a Controller Action
<!-- /stories/:story_id --> <script type="text/x-handlebars" data-template-name="stories/show"> <button {{action edit}} class="btn btn-primary">Edit</button> <h3>{{title}}</h3> {{outlet}} </script>
File: index.html
Finishing Touches
Task - Make editing work from all Sections
Explictly define an ApplicationController (application scope controller)
App.ApplicationController = Ember.Controller.extend();
File: js/controllers.js
Finishing Touches
Task - Make editing work from all Sections
Implement a StoriesShow Controller which correctly transitions to the either stories.edit or stories.edit.sections Routes
App.StoriesShowController = Ember.ObjectController.extend({ needs: "application", actions: { edit: function() { if (this.get("controllers.application.currentPath") === "stories.show.sections") { this.transitionToRoute("stories.edit.sections"); } else { this.transitionToRoute("stories.edit"); } } }, })
File: js/controllers.js
Finishing Touches
Task - Make editing work from all Sections
Add Section Choices to section-edit or storiesEditSections depending on which section we're on.
Edit the top part of storiesEditSections to look like this:
App.StoriesEditSectionsController = Ember.ObjectController.extend(); App.ChoiceAddController = Ember.ObjectController.extend({ needs: ["storiesEditSections","section-edit"], ...
File: js/controllers.js
Finishing Touches
Task - Make editing work from all Sections
Add Section Choices to section-edit or storiesEditSections depending on which section we're on.
... and change the parentController function to be this:
App.ChoiceAddController = Ember.ObjectController.extend({ ... parentController: function() { section = this.get("controllers.storiesEditSections.choices"); if (section === undefined) { section = this.get("controllers.section-edit.choices"); } return section; }, ...
File: js/controllers.js
Finishing Touches
Task - Make 'New Story' work with choices
Implement StoriesNew route
App.StoriesNewRoute = Ember.Route.extend({ model: function(params) { story = this.get("store").createRecord("story"); section = this.get("store").createRecord("section"); story.set("first_section", section); return story; } });
File: js/controllers.js
Finishing Touches
Finito!

Problems? Compare with working version:
$ git diff -w 77fc2 index.html js
Greg Malcolm github.com/gregmalcolm @gregmalcolm
So Long And Thanks For All The Javascripts!
Ember.js Workshop
By gregmalcolm
Ember.js Workshop
- 2,937