Your browser doesn’t support the features needed to display this presentation.

A simplified version of this presentation follows.

For the best experience, please use one of the following browsers:

An Introduction to EmberJS

Tech Camp Memphis


@joshwlewis - @heroku

Tech Camp 2014

Yeah, that's cool...

...for 2012

The framework for ambitious web applications

Convention over Configuration

Ember.js is opionated....

...or maybe we could call it curated.

Conventions for

Developer Happiness

Some other great stuff

Ember is a giant toolbox

Tech Camp 2015


Responsible for mapping paths to your code.

// app/router.js
import Ember from 'ember';
import config from './config/environment';

var Router = Ember.Router.extend({
  location: config.locationType
}); {
  this.route('user', { path: '/:name' });

export default Router;

Model (via Ember Data)

The model knows how to fetch and update your server data.

$ curl
[{"id":10,"name":"Mr. Meow","beenz":4,"gravatar_url":"..."}]
// app/models/user.js
import DS from 'ember-data';

export default DS.Model.extend({
  name: DS.attr('string'),
  beenz: DS.attr('number', { defaultValue: 3 }),
  gravatar_url: DS.attr('string'),


Routes are responsible for loading or syncing data with the server.

// app/routes/application.js
import Ember from 'ember';

export default Ember.Route.extend({
  model() {


Templates automagically bind to your data

<!-- app/templates/application.hbs -->
<div class="user-list list-group">
  {{#each model as |user|}}
    {{#link-to "user" class="user-list-item rainbow list-group-item"}}
      <div class="avatar">
        {{beenz-gravatar url=user.gravatar_url}}
      <div class="info">
        <div class="name">{{}}</div>
        <div class="beenz">{{beenz-kitties beenz=user.beenz}}</div>


<!-- from any template -->
{{ember-kitty image=3 size=40}}
// app/components/ember-kitty.js
export default Ember.Component.extend({
  tagName: "img",
  classNames: ["kitty"],
  attributeBindings: ["src", "height", "width"],
  src: Ember.computed("image", function() {
    return `/assets/images/kitty-${this.get('image')}.png`;
  height: Ember.computed.reads("size"),
  width: Ember.computed('height', function() {
    return Math.round(this.get("height") * 1.17);

Composing components

// app/components/beenz-kitties.js
export default Ember.Component.extend({
  tagName: 'span',
  classNames: ['kitties'],

  kitties: Ember.computed('beenz', function() {
    return [1,2,3,4,5].map((been) => {
      return {
        image: been <= this.get("beenz") ? been : 0,
        been:  been
<!-- app/templates/components/beenz-kitties.hbs -->
{{#each kitties as |kitty|}}
  {{beenz-kitty image=kitty.image been=kitty.been}}

Component Actions

<!-- app/templates/components/give-beenz.hbs -->
{{beenz-kitties beenz=beenz action="giveBeenz"}}
{{#if isEditing}}
  <button {{action 'save'}}>Save Beenz</button>
  <button {{action 'edit'}}>Change Beenz</button>
// app/components/give-beenz.js
export default Ember.Component.extend({
  actions: {
    edit() {
      this.set('isEditing', true);
    save() {
      this.set('isEditing', false);
      this.sendAction('action', this.get('beenz'));
    giveBeenz(been) {
      this.set('beenz', been);

Persisting Data

export default Ember.ObjectController.extend({
  actions: {
    giveBeenz(beenz) {
      const rating = this.get('model.rating')
      rating.set('beenz', beenz); => {
        this.flashMessages.success(`Gave ${beenz} beenz :)`);
      }, () => {
        this.flashMessages.warning("Error giving beenz :(");


Resources for this talk:


Come visit us at Memphis Ruby: