Ember JS: Adding posts to our blog application

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)

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>