The first post in this series, Ember.js Hello World, 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 tiny!
The source code for the completed example can be found on GitHub: justin808/ember-js-guides-railsonmaui-rails4. I carefully crafted the commits to explain the steps.
You can try out the application on Heroku at: http://railsonmaui-emberjs-rails4.herokuapp.com/
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.
Key Tips
- Be sure to update the
ember
andember-data
javascript files with 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
andsetupController
, you can have some very confusing results (details below). - 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 commit.
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 simple.
Basic Setup
I started off with the instructions here The No Nonsense Guide to Ember.js on Rails. 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", "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 http://0.0.0.0:3000/assets/ember.js?body=1:27903:7 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 README:
1
|
|
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 Hello World
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.
1 2 |
|
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.
app/models/post.rb
1 2 3 |
|
app/serializers/post_serializer.rb
1 2 3 |
|
app/controllers/posts_controller.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
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
andsetupController
on the Route! If you do, you’ll get this error:Uncaught Error: assertion failed: Cannot delegate set('title', a) to the 'content' property of object proxy
: 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.1 2 3 4 5 6
App.PostsNewRoute = Ember.Route.extend( model: -> App.Post.createRecord(publishedAt: new Date(), author: "current user") setupController: (controller) -> # controller.set('content', App.Post.createRecord(publishedAt: new Date(), author: "current user")) )
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.
1 2 3 4 5 6 7 8 9 10 |
|
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.
Here’s the commit at github, and the commit description:
See discussion at http://stackoverflow.com/questions/14705124/creating-a-record-with-ember-js-ember-data-rails-and-handling-list-of-record 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.
This is a really important change that is well documented in the commit as well as the tutorial screencast at 36:20.
Heroku Deployment
Heroku has listed many tips at Getting Started with Rails 4.x on Heroku. And you
can look at the commits leading up to tag heroku
. The basic steps are:
- Change a few gems
- Switch from sqllite to postgres.
- Add a ProcFile to use Puma for the webserver.
- Be sure that production.rb contains:
1
config.ember.variant = :production
If you don’t, you’ll see this error:
RAILS_ENV=production bin/rake assets:precompile rake aborted! couldn't find file 'handlebars' (in /Users/justin/j/emberjs/ember-js-guides-railsonmaui-rails4/app/app/assets/javascripts/application.js:18)
Examples that Inspired this Tutorial
RailsCasts
- The two RailsCasts episodes complement the first tutorial by Tom Dale by
showing how to add persistence via the
rails-ember
gem. The serializers episode is also useful. - 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 schemes.
ember_data_example
- ember_data_example on GitHub is a nice full featured ember app with a parent child relationship of contacts and phone numbers. It even has some examples of using Konacha for testing Ember JavaScript code.
Source Code for Views and JavaScript
I purposefully kept these to just 2 files to make this example simple. In a real world application, this would be broken into many files.
View Code: app/views/static/index.html.erb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
|
CoffeeScript: app/assets/javascripts/app.js.coffee.
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 separate files.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
|
Conclusion
Ember does quite a lot with just a few lines of code. Definitely check out the source code for the completed example github: justin808/ember-js-guides-railsonmaui-rails4. Please take a look at the screencast, as I put many details beyond this article.
I welcome comments and suggestions.
This is a companion discussion topic for the original entry at http://www.railsonmaui.com//blog/2013/06/11/emberjs-rails4-tutorial/