The first post in this series, Ember.js Hello
shows Ember working without a persistence backend. This post covers
setting up Rails4 as the persistence engine behind that example, plus
adding and deleting records. The amount of Ember and Rails code to make
this example is almost completely included in this article. It's that
I put many more details in this comprehensive
screencast of how to go from a brand new
Rails 4 app to an Ember.js app deployed on Heroku.
the command from the ember-rails gem (see below). Keeping these
files at appropriate versions is key while the API is changing,
especially for ember-data.
If you specify the Router property for both model and
setupController, you can have some very confusing results (details
Get comfortable with Ember's naming conventions. Ember does a ton
with default naming. It's basically got the same philosophy of
"Convention over Configuration" of Rails. So it's especially
important to try to grok when the Ember examples are doing something
implicitly versus explicitly. This is a bit like Rails. At first it
seems like magic, like "How the heck is that happening", and then
one gets accustomed to the naming conventions and appreciates how
much code it saves.
Be mindful that some Ember.js commands run asynchronously, such as
Building the Hello World without Persistence
The steps for this can be found in the git history up to tag
no-persistence. Thanks to a few gems, the process is relatively
I started off with the instructions here The No Nonsense Guide to
This article covers the basic setup, such as gems to include. You want
to pay special attention to the README for
ember-rails. Depending on the
current state of the ember-rails gem, you may get the deprecation
warning (browser console) with the old ember-data.js.
DEPRECATION: register("store"undefined "main") is now deprecated in-favour of register("store:main");
at Object.Container.register (http://0.0.0.0:3000/assets/ember.js?body=1:7296:17)
at Application.initializer.initialize (http://0.0.0.0:3000/assets/ember-data.js?body=1:5069:19)
at visit (http://0.0.0.0:3000/assets/ember.js?body=1:27041:3)
at DAG.topsort (http://0.0.0.0:3000/assets/ember.js?body=1:27095:7)
at Ember.Application.Ember.Namespace.extend.runInitializers (http://0.0.0.0:3000/assets/ember.js?body=1:27900:11)
at Ember.Application.Ember.Namespace.extend._initialize (http://0.0.0.0:3000/assets/ember.js?body=1:27784:10)
at Object.Backburner.run (http://0.0.0.0:3000/assets/ember.js?body=1:4612:26)
at Object.Ember.run (http://0.0.0.0:3000/assets/ember.js?body=1:5074:26)
Originally, I included a separate version of ember-data in the git
repository. Instead, I should have updated the versions of ember and
ember-data with this command from the ember-rails
rails generate ember:install --head
This command puts the ember files in vendor/assets/ember. Pretty
sweet. This is way better than manually installing the js files.
Get the no-database fixture example of Ember.js working.
Next, I migrated the non-rails static example presented in Ember.js
to the rails framework. You can checkout the tag no-persistence and
get the code to where the static fixture is used and there is no
persistence. Scroll to the bottom to see this code, as well as some
additional code added for persistence.
Building the Hello World with Persistence
Create the Model for Blog Posts
You can checkout the git tag persistence-emberjs to get the git
repository to the state that persistence works.
$ rails generate model Post title:string author:string published_at:date intro:text extended:text
$ rake db:migrate
Since Rails comes pre-configured with sqllite3 by default, no database
configuration is required.
Add the Controller and Serializer
Note that in Rails 4, you need to use the form for "strong parameters".
See the definition of post_params below.
respond_to :json# default to Active Model Serializersdefindex
respond_with Post.update(params[:id], post_params)enddefdestroy
params.require(:post).permit(:title,:intro,:extended,:published_at,:author)# only allow these for nowendend
Adding "Add" and "Remove" Buttons
To create a new post, use a link, not a button, because we want
to change the URL.
Don't define both model and setupController on the Route! If
you do, you'll get this error:
Uncaught Error: assertion failed: Cannot delegate set('title'undefined a) to the 'content' property of object proxy <App.PostsNewController:ember392>: its 'content' is undefined.
I originally had code like this and it took me some time to figure
out that the model part was not used.
Update the URL on New with transitionAfterSave Hook
You can't update the URL after a new record is saved directly in the
event handler, as the commit will run asynchronously, and until the
return value, there is no record id, and you would end up using record
id null in the URL. Here's how to handle this situation. Not that the
save does the commit, but the transitionToRoute is not called until
the transitionAfterSave hook is run.
transitionAfterSave:(-># when creating new records, it's necessary to wait for the record to be assigned# an id before we can transition to its route (which depends on its id)@transitionToRoute('post',@get('content'))if@get('content.id')).observes('content.id'))
Don't put the new record, unsaved post in the list of saved posts
There's a slight bug in the adding of new records. If you click on the
unsaved post link on the left, the URL will have "null" as the new post
does not yet have an ID.
See discussion at
Note the change from iterating over "each model" to iterating over
"each post in filteredContent" in index.html.erb. That change requires
attributes be referenced by "post", and the updated linkTo takes the
route, "post", as well as the "dynamic segment" which is also named
"post", per the above #each post. (refer to
http://emberjs.com/guides/templates/links/). Note the addition of
the PostsController. Previously, it was implicitly defined. It listens
to property "arrangedContent.@each" so that when the new post saves,
the filteredContent property updates and notifies the view template
using this property in index.html.erb. Without the listener on this
property, the view of all posts would not update.
Tip: Using Chrome to watch the videos: I found that the left/right
arrow and space bar keys are amazing for pausing and rewinding the
RailsCasts so that I could get all the nuances of the Ember naming
Here's the entire set of CoffeeScript to build this application. As you
can see, it's not much! I intentionally left this in one file to make
the example a bit simpler. A real application would break this out into
App.Store =DS.Store.extend(revision:12adapter:"DS.RESTAdapter"# "DS.FixtureAdapter")
App.PostsRoute = Ember.Route.extend(model:->
App.Post.find())# See Discussion at http://stackoverflow.com/questions/14705124/creating-a-record-with-ember-js-ember-data-rails-and-handling-list-of-record
App.PostsController = Ember.ArrayController.extend(sortProperties:["id"]sortAscending:falsefilteredContent:(->
content = @get("arrangedContent")
App.PostsNewRoute = Ember.Route.extend(model:->
App.PostsNewController = Ember.ObjectController.extend(save:->
@transitionToRoute('posts')transitionAfterSave:(-># when creating new records, it's necessary to wait for the record to be assigned# an id before we can transition to its route (which depends on its id)
@transitionToRoute('post', @get('content'))if @get('content.id')).observes('content.id'))
App.PostController = Ember.ObjectController.extend(isEditing:falseedit:->@set"isEditing",truedelete:->if(window.confirm("Are you sure you want to delete this post?"))
App.IndexRoute = Ember.Route.extend(redirect:->@transitionTo"posts")
Ember.Handlebars.registerBoundHelper "date",(date)->moment(date).fromNow()window.showdown =newShowdown.converter()
Ember.Handlebars.registerBoundHelper "markdown",(input)->newEmber.Handlebars.SafeString(window.showdown.makeHtml(input))if input # need to check if input is defined and not null
Ember does quite a lot with just a few lines of code. Definitely check
out the source code for the completed example github:
Please take a look at the screencast, as I put many details beyond this
I welcome comments and suggestions.
CEO at ShakaCode
Share this article
Are you looking for a software development partner who can
develop modern, high-performance web apps and sites?