At the end of the last article I challenged you to add the ability to create new posts for our very simple blog application. We saw how to create new comments for a post and adding a new post uses a similar pattern. First we need a route for posts/new. Change the posts resource to include this
this.resource('posts', function() { this.route('new'); });
This means that ember will now create the routes posts
, posts.index
and posts.new
. Previously we only had the posts
route but when we go to /posts
we will now transition to posts.index
. We need to add a posts directory under app/javascripts/templates/
as well as handlebars files for the index and new routes.
/templates/posts/ /templates/posts/index.hbs /templates/posts/new.hbs
These additional new routes means that any sub route of posts
will be rendered within the posts template outlet. We need to add an {{outlet}} to it to allow this to happen. At the same time we need to move the handlebars code for showing the posts from the original posts
template to the posts.index
template.
The posts
template now only contains
{{outlet}}
The posts.index
template needs to contain the old content from the posts template
Posts <ul> {{#each controller}} <li>{{#linkTo post this}}{{title}}{{/linkTo}}</li> {{/each}} </ul>
We will also add a content property to the Post
model to reflect what the rails side expects.
App.Post=DS.Model.extend({ comments: DS.hasMany('App.Comment'), title: DS.attr('string'), content: DS.attr('string') });
In hindsight using content was a poor choice since each controller has a ‘content’ propery and this could get a little confusing. However we will stick with it for the moment.
Just like in the comments.new
template we need a form to submit with the details for our new post. In the posts.new
template add the following
<form {{action save content on='submit'}}> {{view Ember.TextArea valueBinding="title" placeholder="Enter title here"}} {{view Ember.TextArea valueBinding="content.content" placeholder="Enter content here"}} <button type="submit">Create Post</button> </form>
You can see we have content.content
in the second TextArea
, this is because using content alone binds to the controllers ‘content’ rather than the models.
One problem here is that ember does not know what models to use in the posts.index
route. We need to tell it.
Create a PostsIndexRoute
using the following code or rename the PostsRoute to PostsIndexRoute within router.js
App.PostsIndexRoute=Ember.Route.extend({ model: function(){ return App.Post.find(); } });
Add a link to post.new
at the bottom of the posts.index
template
{{#linkTo posts.new}}New Post{{/linkTo}}
We need to tell the PostsNewRoute
how to set up the controller and we also require a controller to handle the save action. Add the following code to the router.js file.
App.PostsNewRoute=Ember.Route.extend({ model: function(){ return App.Post.createRecord(); } });
Inside the model hook we return a new Post model to the controller. Create controllers/postsNewController.js
with the following code
App.PostsNewController=Ember.ObjectController.extend({ save: function(post) { var me = this; post.get("store").commit(); post.on('didCreate', function() { me.get('target').transitionTo('posts.index'); }); } });
The reason we have the post.on('didCreate', function() {
part is so that we do not transition to the posts.index
route before the model has been saved on the server side and has an id. Otherwise we will render the page with a link to /posts/null
.
NOTE 8/7/13 : I have just noticed that the post with a null id is still shown on the index page, what we need to do is change the response format for new posts and comments to stop this. In the rails side PostsController we need the create method to respond with the following { “post”: {“id”: 1, “text”: “blah”….} and similarly for the CommentsController. In the create action under if @post.save
change
format.json { render :json => @post, :status => :created, :location => @post }
to
format.json { render :json => { :post => @post.as_json}, :status => :created, :location => @post }
Change the CommentsController
create method in a similar fashion. We also need the 'didCreate'
listener hook for comments save method on the ember side like we already have for the posts.
The code for this article is available here.
(Code recently updated due to ember/jquery-rails/handlebars-assets version compatibility issues and the json response format changes noted above)