Ember.js for Rails
backend
developers

Miguel Camba

About me

From Rails to Ember.

Yehuda Kats, it’s creator, is also member of the Rails Core Team and the jQuery Core Team.

Ember has borrowed many ideas from Rails and applied them on the frontend development, but with a different twist.

The good parts



The bad parts

Rails MVC

Rails MVC flow

  1. One router recognises the url.
  2. One controller handles the request.
  3. One controller read/creates/modifies models.
  4. Models store the state of your application into the models.
  5. One Controller renders views and send back the resulting html.
  6. And the we forget everything and start over again.


    The keywords here are one and state

Ember MVC

Ember MVC (aka Ember WTF?!)

I know that feeling.

The issue here: Naming

Forget everything you know. MVC is not longer MVC

Rails Ember
Router Router
Models Models
Routes Controllers (meeeh)
Controllers Controllers/Decorators (barely)
Views ::not found::
Templates Views

Ember: Managing long-living state


Backend MVC is all about separation of concerns and HTTP (stateless protocol).


Frontend MVC is all about handing user interaction and managing state.


2 kind of state

Persistent state

Local/application state

Routes

      App.TrainingsRoute = Ember.Route.extend({
        // Find the record(s) and injects them into the controller
        model: function() {
          return this.get('store').find('training');
        },

        // Several hooks for control the application state's flow.
        afterModel: function(trainings, transition) {
          if (posts.length === 0) {
            this.transitionTo('trainings.new');
          }
        }
      })

Controllers

    App.AwardsController = Ember.ArrayController.extend({
      minSlots: 6,
      sortProperties: ['date'],
      leagueAwards: Em.computed.filterBy('content','type', 'league'),
      actions: {
        shareOnFacebook: function(){ /**/ }
      }
    })

Views

    App.BidderView = Ember.View.extend({
      classNameBindings: ['isFriend:friend', 'isMe:me', 'isWinning:winner'],
      classNames: ['bidder'],
      label: function() {
        return this.get('isWinning') ? I18n.t('market.winning_short') : this.get('index') + 1;
      }.property('isWinning', 'index'),
      click: function() {
        this.get('controller').send('showReport');
        return false;
      }
    });

Models

No surprises here, they work as you would expect.

Ember-data =~ ActiveRecord. (the “official” adapters)

  App.Player = DS.Model.extend({
    // Attributes
    name:               DS.attr('string'),
    surname:            DS.attr('string'),
    club:               DS.attr('string'),
    birthdate:          DS.attr('date'),
    // Computed properties
    fullName: function() {
      var  name = this.get('name'),
        surname = this.get('surname');
      if (name && surname) { return name + " " + surname; }
    }.property('name', 'surname')
  }

Templates

Build the markup of your views loading data from views or controllers.

Keep your views updated with bindings.

Handlebars is the official template engine, like ERB for Rails.

  <div class="texts">
    <div class="player-flag">
      
    </div>
    <div class="name"></div>
    <div class="location-age">
      <span class="muted"></span>:
      <span></span>
    </div>
  </div>

Convention over configuration

App.Router.map(function(){
  this.route('dashboard');
  this.resource('config', function(){
    this.route('notifications');
  });
});
App.IndexRoute = Ember.Route.extend({ ... })
App.DashboardRoute = Ember.Route.extend({ ... })
App.ConfigRoute = Ember.Route.extend({ ... })
App.ConfigIndexRoute = Ember.Route.extend({ ... })
App.ConfigNotificationsRoute = Ember.Route.extend({ ... })
App.IndexController = Ember.Controller.extend({ ... })
App.DashboardController = Ember.Controller.extend({ ... })
App.ConfigController = Ember.Controller.extend({ ... })
App.ConfigIndexController = Ember.ObjectController.extend({ ... })
App.ConfigNotificationsController = Ember.ObjectController.extend({ ... })

Convention over configuration

App.Router.map(function(){
  this.resource('config', function(){
    this.route('notifications');
  });
});
<script data-template-name="config"></script>
<script data-template-name="config/index"></script>
<script data-template-name="config/notifications"></script>

Demo!

Thank you!

Q&A