99 Reasons for Using Ember.js

A talk given by Stefan Haslinger
at the Vienna.js Meetup July 30th, 2014
online at http://at.mittenin.at/36

Stefan

@informatom
stefan.haslinger@informatom.com
I'm 50% of http://mittenin.at
I'm 100% of http://informatom.com

Part I

The Library

MVC Architecture

...not

Core Concepts

RMC

Router - Model - Controller

or even ...

RMCVLTCT

Router - Model - Controller - View - Layout - Template - Components -Template

Router

            
Contracting.Router.map(function() {
  this.resource("contracts", { path: "/" });
});
            
          

Routes and Resources

            
App.Router.map(function() {
  this.resource('posts', { path: '/posts' }, function() {
    this.route('new');
  });
});
            
          

nested Resources

i.e. controllers can be nested
            
Contracting.Router.map(function() {
  this.resource("contracts", { path: "/" }, function() {
    this.resource("contract", { path: "/contract/:contract_id" }, function() {
      this.resource("contractitem", { path: "/item/:contractitem_id" }, function() {
        this.resource("consumableitem", { path: "/consumable/:consumableitem_id" });
      });
    });
  });
});
            
          

Ember Object

            
App.Person = Ember.Object.extend({
  say: function(thing) {
    alert(thing);
  }
});
            
          

Computed Properties

            
App.Person = Ember.Object.extend({
  // these will be supplied by `create`
  firstName: null,
  lastName: null,

  fullName: function() {
    return this.get('firstName') + ' ' + this.get('lastName');
  }.property('firstName', 'lastName')
});

var ironMan = App.Person.create({
  firstName: "Tony",
  lastName:  "Stark"
});

ironMan.get('fullName'); // "Tony Stark"
            
          

Ember Model

            
App.Person = DS.Model.extend({
  firstName: DS.attr(),
  lastName: DS.attr(),
  birthday: DS.attr()
});
            
          

Ember Controller

            
App.SongController = Ember.ObjectController.extend({
  soundVolume: 1
});
            
          

Actions

            
App.PostController = Ember.ObjectController.extend({
  isExpanded: false,

  actions: {
    expand: function() {
      this.set('isExpanded', true);
    },

    contract: function() {
      this.set('isExpanded', false);
    }
  }
});
            
          

Action Handler

Actions are bubbling

Action Bubbling

Objectcontollers

for single Objects
            
App.SongController = Ember.ObjectController.extend({
  soundVolume: 1
});
            
          

Arraycontrollers

for arrays of objects
and Itemcontroller to refer to a single object's controller
            
App.SongsController = Ember.ArrayController.extend({
  sortProperties: ['name', 'artist'],
  sortAscending: true // false for descending,
  itemController: 'song'
});
            
          
adds template features like
            
{{#each controller}}
  
  • {{fullName}}
  • {{/each}}

    Ember View

                
    var view = Ember.View.create({
      templateName: 'say-hello',
      name: "Bob"
    });
                
              

    Ember Template

                
    
                
              

    Handlebars

                
    {{#if person}}
      Welcome back, {{person.firstName}} {{person.lastName}}!
    {{else}}
      Please log in.
    {{/if}}
                
              

    Run Loop

                
    Ember.run.queues
    // => ["sync", "actions", "routerTransitions", "render", "afterRender", "destroy"]
                
              

    Observers

                
    Person = Ember.Object.extend({
      firstName: null,
      lastName: null,
    
      fullName: function() {
        var firstName = this.get('firstName');
        var lastName = this.get('lastName');
    
        return firstName + ' ' + lastName;
      }.property('firstName', 'lastName'),
    
      fullNameChanged: function() {
      }.observes('fullName').on('init')
    });
    
    var person = Person.create({
      firstName: 'Yehuda',
      lastName: 'Katz'
    });
    
    person.set('firstName', 'Brohuda'); // observer will fire
                
              

    Event Handling

    Bubbling and sending
                
    App.ClickableView = Ember.View.extend({
      click: function(evt) {
        alert("ClickableView was clicked!");
        this.get('controller').send('turnItUp', 11);
      }
    });
                
              
                
    {{#view App.ClickableView}}
    This is a clickable area!
    {{/view}}
                
              

    Data Binding

                
    wife = Ember.Object.create({
      householdIncome: 80000
    });
    
    husband = Ember.Object.create({
      wife: wife,
      householdIncome: Ember.computed.alias('wife.householdIncome')
    });
    
    husband.get('householdIncome'); // 80000
    
    husband.set('householdIncome', 90000);
    wife.get('householdIncome'); // 90000
                
              

    Logging

                
    App = Ember.Application.create({
      LOG_TRANSITIONS: true
    });
                
              

    Debugging Features

                
    {{log record}}
                
              

    Query Params

    http://example.com/articles?sort=ASC&page=2

    Enumerables

                
    var words = ["goodbye", "cruel", "world"];
    
    var emphaticWords = words.map(function(item) {
      return item + "!";
    });
    // ["goodbye!", "cruel!", "world!"]
                
              

    1.6 is out

    rewritten to use ES6 modules

    1.6+ builds are now transpiled by the es6-module-transpiler into AMD
    and use a small AMD loader to handle loading the transpiled modules

    Part 2

    The Ecosystem

    Easy Ruby On Rails Integration

    Ember Rails

    Emberkit

    commercial, pricy
    Presentation on Youtube

    Ember Data

                
    App.Store = DS.Store.extend();
                
              
                
    this.store.find('blogPost');
    this.store.find('blogPost', 142);
                
              

    Relations between Models

                
    App.BlogPost = DS.Model.extend({
      title: DS.attr(),
      createdAt: DS.attr('date'),
    
      comments: DS.hasMany('comment')
    });
    
    App.Comment = DS.Model.extend({
      body: DS.attr(),
      username: DS.attr(),
    
      post: DS.belongsTo('blogPost')
    });
                
              

    Caching

    finding a loaded record
    finding a record already loaded

    Ember Rest Adapter

                
    var post = store.find('post', 1);
                
              

    Active Model Adapter

                
    App.FamousPerson = DS.Model.extend({
      firstName: DS.attr('string'),
      lastName: DS.attr('string'),
      occupation: DS.attr('string')
    });
                
              
    expects JSON in the form of
                
    {
      "famous_person": {
        "first_name": "Barack",
        "last_name": "Obama",
        "occupation": "President"
      }
    }
                
              

    Active Model Serializer

                
    class PostSerializer < ActiveModel::Serializer
      attributes :title, :body
    end
    
    class PostsController < ApplicationController
      def index
        @posts = Post.all
        render json: @posts
      end
    end
                
              
    creates JSON suitable for Active Model Adapter
                
    {
      "posts":
        [
          { "title": "Post 1", "body": "Hello!" },
          { "title": "Post 2", "body": "Goodbye!" }
        ]
    }
                
              

    EPF

    Ember Persistance Framework
    RESR Sync
    syncing between Server, Store and Client

    Social

    Plays nicely with other libraries
    (e.g. Bootstrap)

    Runs nicely in JSBIN

    Emberjs - JSBIN JSBIN Demo

    Coffeescript

                
    Contracting.Contract = DS.Model.extend
      contractitems: DS.hasMany("contractitem",
        async: true
      )
    
      term: DS.attr("number")
      startdate: DS.attr("date")
      createdAt: DS.attr("date")
      updatedAt: DS.attr("date")
    
      enddate: (->
        moment(@get("startdate")).add("months", @get("term"))
                                 .subtract "days", 1
      ).property("startdate", "term")
    
      positions: Ember.computed.mapBy('contractitems', 'position')
      maxposition: Ember.computed.max('positions')
                
              

    Emberscript

                
    class PostsController extends Ember.ArrayController
    
      trimmedPosts: ~>
        @content?.slice(0, 3)
    
      +observer content.@each
      postsChanged: ->
        console.log('changed')
                
              

    Emblem.js

                
    p Introducing Emblem.js: a new templating language
      that compiles to Handlebars.js
    
    ul
      each person in people
        li = person.name
      li Indentation-based (like Slim, Jade, HAML, etc.)
      li Compiles to Handlebars; full compatibility with
         both custom and built-in Handlebars helpers
    
    section.ember-features
      h1 class=foo The class name of this element is bound to `foo`
      p class=isActive:flashing:inactive Ember.js bound css classes
    
      a click="doIt" href="#" Hello, how are you doing, #{name}?
    
    #footer: ul.menu-items: each menu_items: li: a.menu-link href=url: link_text
    
    if something
      p something was true!
    else
      p something was false!
                
              

    Can be tested

    QUnit + Helpers

    The computed property
                
    App.SomeThing = Ember.Object.extend({
      foo: 'bar',
      computedFoo: function(){
        return 'computed ' + this.get('foo');
      }.property('foo')
    });
                
              
    can be unit tested in a few lines of code
                
    module('Unit: SomeThing');
    
    test('computedFoo correctly concats foo', function() {
      var someThing = App.SomeThing.create();
      someThing.set('foo', 'baz');
      equal(someThing.get('computedFoo'), 'computed baz');
    });
                
              

    Promises

    can be used seperately as RSVP.js
                
    var promise = fetchTheAnswer();
    
    promise.then(fulfill, reject);
    
    function fulfill(answer) {
      console.log("The answer is " + answer);
    }
    
    function reject(reason) {
      console.log("Couldn't get the answer! Reason: " + reason);
    }
                
              

    Promises with concurrency

    explained in Stefan Penner's talk The Promise Land

    Build Tools

    Ember CLI

    Extensible

    Ember Addons

    Ember Components

    Ember.I18n

                
    Em.I18n.translations = {
      'user': {
        'edit': {
          'title': 'Edit User'
        },
        'followers': {
          'title': {
            'one': 'One Follower',
            'other': 'All {{count}} Followers'
          }
        }
      },
      'button': {
        'add_user': {
          'title': 'Add a user',
          'text': 'Add',
          'disabled': 'Saving...'
        }
      }
    };
                
              

    Ember Table

    Ember Charts

    Ember Widgets

    Ember Forms

    Bootstrap for Ember

    Torii

    supported by CDNJS

    Requires minimal Webspace 'cause of Cloudflare

    Ember Inspector

    for Chrome & for Firefox

    Building Chrome Extensions

    YouTube Video: Building Chrome Extensions With EmberJS

    Part 3

    The Philosophy behind

    Convention over Configuration

    e.g. naming conventions

    Clean Object Orientation

    Single Page Apps

    Stable API

    ... since Ember 1.0 is out

    Clean Browser History

    Goes forward to ...

    HTMLBARS

    This is our ideal, right?
                
    
    {{bar}}
    but this is what we have to do in Ember today

    With HTMLBars, we get our ideal syntax!
                
    
    {{bar}}

    Will be compatible to ...

    Web Components

    Components

    ... can be built already

                
    
                
              
    can be called:
                
    

    My Blog

    {{#each}} {{blog-post}} {{/each}}

    No transactions

    ... any more

    Super readable code

                
    Contracting.Contract = DS.Model.extend
      contractitems: DS.hasMany("contractitem",
        async: true
      )
    
      term: DS.attr("number")
      startdate: DS.attr("date")
      createdAt: DS.attr("date")
      updatedAt: DS.attr("date")
    
      enddate: (->
        moment(@get("startdate")).add("months", @get("term"))
                                 .subtract "days", 1
      ).property("startdate", "term")
    
      positions: Ember.computed.mapBy('contractitems', 'position'),
      maxposition: Ember.computed.max('positions')
                
              

    Robust Security Policy

    Part 4

    The Community

    Friendly and inclusive Community

    Ember Guides

    API Documentation

    Ember Core Team

    Yehuda Kats

    Katz Got Your Tongue?

    Member of ...

    W3C TAG

    Technical Architecture Group

    Ember Fest 2014

    Videos

    Lots of videos already up,
    e.g. Emberconf 2014 Videos

    Ember Conf

    Stackoverflow

    10,812 Questions tagged with Ember.js
    as of July 27th, 2014

    IRC Channel

    irc.freenode.net #emberjs

    Discourse

    Ember Discussion Forum

    Lots of company support

    Through use and support: Ember Users

    Yahoo! - Living Social - Zendesk - Groupon - Travis CI - Boxee - Thoughtbot - Runtastic - Code School

    Ember Weekly

    Books

    The first books are out

    Rock and Roll with Ember.js

    Built with Ember

    Ember Flare

    Community driven place for all things Ember.js

    Tutorials

    e.g. Vic Ramon's ember Tutorial

    Todo MVC Demo

    build by the core team

    Jobs

    Job Postings @ Ember Discussion Formum

    Me

    You can talk to me about Ember!

    Envy

    A whole lot of envy from the Angular Community ;-)

    Tomster
    Tomster

    Online Hangout

    Ember.js Hangout, on YouTube and @emberjshangout

    Ember.js Linz Meetup

    Ember.js Vienna Meetup

    @emberjsvienna

    Next Meetup

    Tuesday, August 5th, 18:30 @ sektor5

    This Presentation

    ... with lots of ressources can be found at
    http://at.mittenin.at/36



    Thanks for having me!

    http://stefan-haslinger.at - @informatom