Github small

Ember Concepts

We could start coding right away, but I think that if you haven’t seen Ember-flavored javascript before then the code might not make much sense. So before we dive into building out the app I’m going to cover some core Ember concepts and a few of the Ember objects that you’ll be seeing.

If you are dying to code you can skip directly the Our App chapter, but if you want to know what’s going on then I recommend that you stick around.

The following few chapters will be a whirlwind tour of Ember Objects, Routing, Routes, Controllers, Views, and Templates. I’m only going to cover the basics so we can start coding as soon as possible. You can do a deep dive through the Ember Guides once you’ve completed this tutorial.

The Ember Object

Ember implements its own object system. The base object is Ember.Object. All of the other objects in Ember extend Ember.Object.

Most of the time you will be using an object that extends Ember.Object like Ember.Controller or Ember.View, but you can also use Ember.Object itself. One common use for Ember.Object is to create service objects that handle some specific logic.

If you open your browser’s console in your Hello World app you’ll be able to follow along with these commands, though you’ll need to convert the CoffeeScript to Javascript (or just click the toggle at the top of the page).

You can instantiate a basic object like this:

user = Ember.Object.create()
var user = Ember.Object.create();

Initialize it with properties by just passing them to create:

user = Ember.Object.create({ firstName: 'Sam', lastName: 'Smith' })
var user = Ember.Object.create({ firstName: 'Sam', lastName: 'Smith' });

You can get a property from the object by calling .get on it and passing the string name of the property:

user = Ember.Object.create({ firstName: 'Sam', lastName: 'Smith' })
user.get('firstName') is 'Sam' #=> true
user.get('lastName') is 'Smith' #=> true
var user = Ember.Object.create({ firstName: 'Sam', lastName: 'Smith' });
user.get('firstName') == 'Sam' //=> true
user.get('lastName') == 'Smith' //=> true

Inquire about the object with .toString(). In this case we see that it’s just Ember.Object.

user = Ember.Object.create();
user.toString() #=> <Ember.Object:ember{objectId}>
var user = Ember.Object.create();
user.toString() //=> <Ember.Object:ember{objectId}>

Defining Objects

So far we’ve just been using Ember.Object. You can create a “subclass” of Ember.Object by using extend. Say we want to make a user Object “class”:

App.User = Ember.Object.extend()
App.User = Ember.Object.extend();

Now you can instantiate a user with create:

user = App.User.create()
var user = App.User.create();

Note that I’m putting this User object inside App. Ember needs to place all of the app data inside a variable, and Ember devs typically use App. So when you define some kind of Object in ember you always want to have it on App.

Now what we’ve done is great and all, but we probably want to add things to our User object.

Objects can have three types of things inside them: properties, functions, and observers. I’ll cover each of these.

Properties

Here’s how you could define a property:

App.User = Ember.Object.extend
  isHuman: true
  temperature: 98.6
  favoriteDirector: 'Tarantino'
App.User = Ember.Object.extend({
  isHuman: true,
  temperature: 98.6,
  favoriteDirector: 'Tarantino'
})

isHuman, temperature, and favoriteDirector would now be accessible with .get.

user = App.User.create()
user.get('isHuman') #=> true
user.get('favoriteDirector') #=> "Tarantino"
user.get('temperature') #=> 98.6
var user = App.User.create();

user.get('isHuman'); //=> true
user.get('favoriteDirector'); //=> "Tarantino"
user.get('temperature'); //=> 98.6

These are the basic versions of Ember properties. We can also create computed properties that actually do some work and call other properites:

App.User = Ember.Object.extend

  fullName: ( ->
    @get('firstName') + ' ' + @get('lastName')
  ).property('firstName', 'lastName')
App.User = Ember.Object.extend({

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

})

Let’s dissect this.

First, we specify the property name with fullName:.

Second, we pass it a function that will return the value we want. We can get at other properties in our object by doing this.get('propertyName'), or in CoffeeScript, @get('propertyName').

Third, we call .property() on our function to tell Ember that it’s a computed property.

Finally, we have to tell property() which other properties this property depends on. It expects a list of the property names as strings. In this case, fullName should change any time firstName or lastName changes, so it needs to watch both of them.

Functions

Functions are quite simple:

App.User = Ember.Object.extend

  showMessage: (message) -> alert(message)

  showName: -> @showMessage(@get('fullName'))
App.User = Ember.Object.extend({

  showMessage: function(message) { alert(message) },

  showName: function() { this.showMessage(this.get('fullName')) }

})

Our showMessage function takes one argument: the message we want to alert. The showName function calls the showMessage function with the fullName of our user (assuming we’ve implemented the fullName property).

Here’s how you actually call a function:

user = App.User.create()
user.showMessage('it works')
var user = App.User.create()
user.showMessage('it works')

Observers

Observers are functions that fire whenever any of the things they observe change. They look like properties but they end with observes() instead of property()

App.User = Ember.Object.extend

  weightChanged: ( ->
    alert('yikes') if @get('weight') > 400
  ).observes('weight')
App.User = Ember.Object.extend({

  weightChanged: function() {
    if (this.get('weight') > 400) alert('yikes')
  }.observes('weight')

})

The above code would fire an alert saying “yikes” whenever the weight property on this user changes and is greater than 400. You can observe as many things as you’d like:

App.User = Ember.Object.extend

  bodyObserver: ( ->
    alert("You've changed. I feel like I don't even know you anymore.")
  ).observes('weight', 'height')
App.User = Ember.Object.extend({

  bodyObserver: function() {
    alert("You've changed. I feel like I don't even know you anymore.");
  }.observes('weight', 'height')

})

This would fire every time weight or height changed.

Extend Existing Objects

The Ember Object system is really great. It makes it easy to write modular, reusable code.

You can extend any existing object in your app like this:

App.Animal = Ember.Object.extend
  likesFood: true

App.Human = App.Animal.extend()

human = App.Human.create()

human.get('likesFood') #=> true
App.Animal = Ember.Object.extend({
  likesFood: true
})

App.Human = App.Animal.extend()

var human = App.Human.create()

human.get('likesFood') //=> true

Properties, functions, and observers in the child object will override those in the parent:

App.Animal = Ember.Object.extend
  likesFood: true

App.Bird = App.Animal.extend
  likesFood: false

bird = App.Bird.create()

bird.get('likesFood') #=> false
App.Animal = Ember.Object.extend({ likesFood: true })

App.Bird = App.Animal.extend({ likesFood: false })

var bird = App.Bird.create()

bird.get('likesFood') //=> false

Extending objects is a pattern you will use all the time while developing in Ember. You can use it to extract out common functionality or pull in functionality from some other object.

Init

All ember objects call an init function when they are first initialized. You can use this to do setup work.

App.Human = Ember.Object.extend

  init: -> alert("I think, therefore I am")
App.Human = Ember.Object.extend({

  init: function() { alert("I think, therefore I am"); }

})

This is fine with basic Ember Objects, but if you are using other, more specific Ember Objects like Route or Controller, then you should try to avoid using init and instead opt for other Ember conventions. If you must use it in one of these objects make sure that you call @_super() in the init function, otherwise you may break things.

This is fine with basic Ember Objects, but if you are using other, more specific Ember Objects like Route or Controller, then you should try to avoid using init and instead opt for other Ember conventions. If you must use it in one of these objects make sure that you call this._super() in the init function, otherwise you may break things.

Reopening Objects

You can go back and add more properties, functions, and observers by calling reopen on the object:

App.Human = Ember.Object.extend()

App.Human.reopen
  name: 'Señor Bacon'

francis = App.Human.create()
francis.get('name') #=> 'Señor Bacon'
App.Human = Ember.Object.extend();

App.Human.reopen({ name: 'Señor Bacon' });

var francis = App.Human.create();
francis.get('name') //=> 'Señor Bacon'

As you’ve seen, base objects can function like classes do in other languages. You can even define a sort of class method on objects by calling reopenClass:

App.Human = Ember.Object.extend()

App.Human.reopenClass
  sayUncle: -> alert("uncle")
App.Human = Ember.Object.extend();

App.Human.reopenClass({
  sayUncle: function() { alert("uncle"); }
})

Then call the class method on the object definition itself:

App.Human.sayUncle()
App.Human.sayUncle();

Ember Objects provide us with the ability to write object-oriented javascript. This is one of Ember’s best features.

These comments support markdown so you can post code.