EmberJS

Yet Another Javascript Thingamajig?

AngularJS vs. Backbone.js

vs. Ember.js vs React

www.airpair.com/js/javascript-framework-comparison

Ember: Holistic approach, embraces MVC structure
Application performance. Saves from writing boilerplate. Favoring convention over configuration
 

Backbone: Minimalism. Small, fast & easy to learn. Provides the minimum (and at times even less than minimum)

 

Angular: innovative approach for extending HTML. Google behind it. Works well both for quick prototyping projects and large-scale production applications

AngularJS vs. Backbone.js

vs. Ember.js vs React

smashingboxes.com/blog/choosing-a-front-end-framework-angular-ember-react

www.sitepoint.com/react-has-won-the-client-side-war/

www.developereconomics.com/comparison-4-popular-javascript-mv-frameworks-part-2/

 

React: lightest of the three (Angular/Ember/React). It can’t even be considered a framework. It does one thing really well: render UI components. Many pair it with Ember/Angular. Common scenario is to use it with a Flux architecture

Trends

Ember: Getting Started

npm install -g ember-cli
ember new my-app

Auto-Updating Handlebars Templates

http://handlebarsjs.com/

  Ember makes Handlebars templates even better, by ensuring your HTML stays up-to-date when the underlying model changes. To get started, you don't even need to write any JavaScript.

<div class="post">
  <h1>By {{author}}</h1>
  <div class="body">{{body}}</div>

  <h1>Comments</h1>

  {{#each comments}}
  <h2>By {{author}}</h2>
  <div class="body">{{body}}</div>
  {{/each}}
</div>

Components

Create your own application-specific HTML tags, using Handlebars to describe their markup and JavaScript to implement custom behavior

<!-- application.js -->
<ul class="example-gravatar">
  <li>{{gravatar-image email="tomster@emberjs.com" size="200"}}</li>
  <li>{{gravatar-image size="200"}}</li>
</ul>
// components/gravatar-image.js
App.GravatarImageComponent = Ember.Component.extend({
  size: 200,
  email: '',
  gravatarUrl: Ember.computed('email', 'size', 
    function() {
      var email = this.get('email').toLowerCase(), 
      size = this.get('size');
      return 'http://www.gravatar.com/avatar/' + md5(email) + '?s=' + size;
  })
});
<!-- components/gravatar-image.hbs -->
<img src={{gravatarUrl}}>
<div class="email-input">
  {{input type="email" value=email placeholder="Enter your Gravatar e-mail"}}
</div>

Core Concepts

Router

When the URL changes, the Ember router maps the URL to a route handler

Route Handlers

  • Renders a template
  • Loads a model that is available to the template

Models

Represent persistent state

Components

  • Control how the user interface behaves
  • Consist of a
    • template written in Handlebars
    • source file written in JavaScript defining behavior

Templates

  • Describe how a user interface looks 
  • Organizing the layout of HTML in an application

Tutorial - Create and Start

# create new project
ember new super-rentals

# project structure
|--app
|--bower_components
|--config
|--dist
|--node_modules
|--public
|--tests
|--tmp
|--vendor

bower.json
ember-cli-build.js
package.json
README.md
testem.json

# start server
ember server

Tutorial - Generate Routes

# create route
ember generate route about

# create another route
ember g route contact

# create main default route
ember g route index

# link pages
{{#link-to "about"}}About{{/link-to}}

# custom path for routes
this.route('bgm', {path: 'b'});

Tutorial - Route Handlers

// load model in route handler 
var rentals = [{
  id: 1,  title: 'Grand Old Mansion',  owner: 'Veruca Salt',  
  city: 'San Francisco',  type: 'Estate',  bedrooms: 15,
  image: 'https://upload.wikimedia.org/wikipedia/commons/c/cb/Crane_estate_(5).jpg'
}, {
  id: 2,  title: 'Urban Living',  owner: 'Mike TV',
  city: 'Seattle',  type: 'Condo',  bedrooms: 1,
  image: 'https://upload.wikimedia.org/wikipedia/commons/0/0e/Alfonso_13_Highrise_Tegucigalpa.jpg'
}, {
  id: 3,  title: 'Downtown Charm',  owner: 'Violet Beauregarde',
  city: 'Portland',  type: 'Apartment',  bedrooms: 3,
  image: 'https://upload.wikimedia.org/wikipedia/commons/f/f7/Wheeldon_Apartment_Building_-_Portland_Oregon.jpg'
}];

export default Ember.Route.extend({
  model() { // model hook called each time the index route is entered
    return rentals;
  },
});
<!-- update index -->

{{#each model as |rental|}}
  <h2>{{rental.title}}</h2>
  <p>Owner: {{rental.owner}}</p><p>Type: {{rental.type}}</p>
  <p>Location: {{rental.city}}</p><p>Number of bedrooms: {{rental.bedrooms}}</p>
{{/each}}

Tutorial - Model

# generate model
ember g model rental
// define the model
export default DS.Model.extend({
  title: DS.attr(),
  owner: DS.attr(),
  city: DS.attr(),
  type: DS.attr(),
  image: DS.attr(),
  bedrooms: DS.attr()
});

Tutorial - Model

# install Mirage and restart ember server
ember install ember-cli-mirage
// app/mirage/config.js:  specify /rentals endpoint
export default function() {
  this.get('/rentals', function() {
    return { data: [{
        type: 'rentals', id: 1, attributes: {
          title: 'Grand Old Mansion', owner: 'Veruca Salt', city: 'San Francisco', type: 'Estate',
          bedrooms: 15, 
          image: 'https://upload.wikimedia.org/wikipedia/commons/c/cb/Crane_estate_(5).jpg'
        }
      }, {
        type: 'rentals', id: 2, attributes: {
          title: 'Urban Living', owner: 'Mike Teavee', city: 'Seattle', type: 'Condo',
          bedrooms: 1, 
          image: 'https://upload.wikimedia.org/wikipedia/commons/0/0e/Alfonso_13_Highrise_Tegucigalpa.jpg'
        }
      }, {
        type: 'rentals',id: 3, attributes: {
          title: 'Downtown Charm', owner: 'Violet Beauregarde', city: 'Portland', type: 'Apartment',
          bedrooms: 3, 
          image: 'https://upload.wikimedia.org/wikipedia/commons/f/f7/Wheeldon_Apartment_Building_-_Portland_Oregon.jpg'
        }}]};});}
// update model hook
  model() {
    return this.store.findAll('rental');
  }

Tutorial - Component

# generate component
ember g component rental-listing
<!-- app/templates/components/rental-listing.hbs -->

<h2>{{rental.title}}</h2>
<p>Owner: {{rental.owner}}</p>
<p>Type: {{rental.type}}</p>
<p>Location: {{rental.city}}</p>
<p>Number of bedrooms: {{rental.bedrooms}}</p>


<!-- index -->

{{#each model as |rentalUnit|}}
  {{rental-listing rental=rentalUnit}}
{{/each}}

Tutorial - Component

// app/components/rental-listing.js
export default Ember.Component.extend({
  isImageShowing: false,
  actions: {
    imageShow() {
      this.set('isImageShowing', true);
    },
    imageHide() {
      this.set('isImageShowing', false);
    }
  }
});
<!-- app/templates/components/rental-listing.hbs -->

{{#if isImageShowing}}
  <p><img src={{rental.image}} alt={{rental.type}} width="500"></p>
  <button {{action "imageHide"}}>Hide image</button>
{{else}}
  <button {{action "imageShow"}}>Show image</button>
{{/if}}

Computed Properties

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

  fullName: Ember.computed('firstName', 'lastName', function() {
    return `${this.get('firstName')} ${this.get('lastName')}`;
  })
});

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

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

Observers

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

  fullName: Ember.computed('firstName', 'lastName', function() {
    return `${this.get('firstName')} ${this.get('lastName')}`;
  }),

  fullNameChanged: Ember.observer('fullName', function() {
    // deal with the change
    console.log(`fullName changed to: ${this.get('fullName')}`);
  })
});

var person = Person.create({
  firstName: 'Yehuda',
  lastName: 'Katz'
});

// observer won't fire until `fullName` is consumed first
person.get('fullName'); // "Yehuda Katz"
person.set('firstName', 'Brohuda'); // fullName changed to: Brohuda Katz

Links

EmberJS

By Timothy Lim