5 Essential Ember Concepts You Must Understand

It’s likely you struggle getting the “big picture” if you just started with Ember.

But even if you do have some experience, even after reading many blogs and perusing the official guides, there are times when you can’t wrap your head around its architecture.

Templates, routes, views, controllers, models, components… they’re still confusing and require clarification. What logic belongs where? What’s the relationship between them?

The Big Picture

The first concept is an overarching one: the Ember object model. Ember objects extend plain Javascript objects to provide core framework functions.

The other four concepts are the fundamental building blocks: routing, models, services and components.

In its simplest form, an Ember app uses the routing layer to resolve, based on the URL, a particular model which is then handed over to a component for display and interaction. A service may be used to retrieve models, for example.

Having a modest idea about how concepts relate to one another, let’s carry on with an example:

What’s going on here?

  1. The Router parses the /items/2 URL and dispatches control to a matching route: ItemRoute with a parameter item_id=2
  2. ItemRoute invokes its model() hook, in which our app returns a model (Item with id=2) fetched via a service (hint: Ember Data!)
  3. renderComponent() is then run to render the component ItemDisplayComponent, passing it the resolved model Item
  4. ItemDisplayComponent is responsible for the user interface: rendering/updating the DOM, and handling browser events

But… renderComponent() does not exist! What are you telling me, Frank?”

Please bare with me! Ember does not have such a method yet, however, Ember will have something like it at some point.

By the end of this article I will show you an implementation of renderComponent().

Great! Now that we understand the flow of a basic application, it’s time to dive deeper into these essential concepts.

This is an article about concepts. If you are new to Ember and want to get your coding hands busy, try this guide.

1. The Object Model

Plain Javascript objects lack the ability to be dependent on other properties changes.

As this is a critical feature, Ember introduced Ember.Object. It augments Javascript objects with computed properties, inheritance, mixins and other useful functions.

This is why we won’t see object.id = "value" but rather object.set('id', "value"). And idioms like:

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

Automatic binding of object properties throughout our application gives us enormous power and flexibility!

Most objects in Ember, including routes, Ember Data models, services and components extend Ember.Object.

2. Routing

Ember is URL-driven so it always starts at the URL. In the example, our Router has the following definition:

// app/router.js
this.route('item', { path: '/items/:item_id' });

Convention over configuration is an important philosphy in Ember.

Whenever we refer to an item route, Ember knows it’s the ItemRoute class and its location (typically app/routes/item.js). If no route is defined, Ember will use an auto-generated in-memory default route.

An instance of ItemRoute will invoke the model() method from where we return our model:

// app/routes/item.js

export default Ember.Route.extend({
  model(params) {
    return {
      id: params.item_id,
      text: "This is item " + params.item_id

In the case above, we’re returning a plain Javascript object. As we’ll see later, there are other more interesting types of objects we can return from model().

After this, our fictitious renderComponent() method will call ItemDisplayComponent with the model data.

Data Down, Actions Up

Not only do routes pass model data down to components. They may also have their actions called by components. These route actions are usually used for application logic involving URL transitions, data-related operations or other route-level concerns.

For instance:

export default Ember.Route.extend({
  actions: {
    remove(item) {
      item.remove().then(() => {

Why do data operations belong in the route? This is simply because in Ember only the owner of a certain object is allowed to manipulate it. The route owns the data since it loaded it.

It is not yet possible to call route actions from a component natively in Ember. However, there is a convenient add-on called ember-route-action-helper that enables us to do exactly that.

Let’s update our diagram to reflect the remove action:

Do you notice that actions go up and data go down? This is an Ember architectural pattern at work: Data Down, Actions Up.

Ember best practices delivered straight to your inbox? Tell me where:

3. Models

Remember the plain Javascript object we returned in the model hook? Models can be much more sophisticated.

A model can be fetched with the Ember.$.getJSON() utility:

export default Ember.Route.extend({
  model() {
    return Ember.$.getJSON('/items.json');

Is this returning a value? No, this is an async call over the network! It returns a Promise.

Promises are objects that represent a future value. They can successfully resolve this value (fulfill) or fail to resolve it (reject).

This is why we were using the word “resolve” in the route: the transition will pause until that promise fulfills or rejects before proceeding with the render phase.

The model() hook will work with almost any type!

Plain objects, Ember objects for sync calls (such as Ember Data’s peekRecord or peekAll) and Promises for async calls like Ember.$.getJSON().

But by far the most common async use-case is Ember Data’s findAll or findRecord:

export default Ember.Route.extend({
  model(params) {
    return this.store.findRecord('item', params.item_id);

The call above will resolve an instance of DS.Model, a specific class all Ember Data models inherit from.

The component will only start rendering model data once the promise has resolved.

4. Services

Did you notice we just called this.store? The way we interact with Ember Data (query, save, etc) is through its store interface.

But how did that store property get in the route?

Nothing special about Ember Data. Under the hood, Ember Data uses Ember’s Injection API to inject its store service into all routes of the application.

An Ember.Service is a long-lived object (singleton) used to provide services to other Ember objects. It directly extends Ember.Object but uses its own class name to encourage the services architecture pattern.

We can create our own custom service and have it injected exactly like the Ember Data store – on any Ember object we want!

Say we have a crypto service. For ad-hoc injection we’d use the following syntax:

export default Ember.Route.extend({
  crypto: Ember.inject.service()

Which will look up a service at app/services/crypto.js and make it available as this.get('crypto').

When moving away from controllers, Services are the appropriate place to hold the singleton state that backs components.

5. Components

Components consist of two parts: a Javascript component file (that defines behavior) and its accompanying Handlebars template.

They are included using curly brace syntax ({{my-component}}) and soon with angle brackets, like <my-component>. Our own custom HTML tags!

Components are isolated by design. They are not aware of their surrounding context, but they receive data through their attributes. Data may include functions (actions), so a component can call route actions as we saw earlier.

From our example:

// app/components/item-display.js

export default Ember.Component.extend({
  isModelTwo: Ember.computed('model.id', function() {
    return this.get('model.id') == 2;
{{!-- app/templates/components/item-display.hbs  --}}

<h1>{{ model.text }}</h1>

{{#if isModelTwo}}
  <p><strong>OMG MODEL TWO!</strong></p>

The component received the model in the route render phase.

It defined a computed property isModelTwo (highlighted blue in the diagram). These properties allow objects to always be up-to-date. One-way data bindings are Ember object model’s magic sauce!

Components naturally may include other components.

<h1>{{i18n-display text=model.text}}</h1>

{{#if isModelTwo}}
  <p><strong>OMG MODEL TWO!</strong></p>

{{ad-items language="en" seed=model remove=(route-action "remove")}}

And those are the five concepts!

What about controllers?

Controllers are still a part of Ember. I strongly recommend you read Should we use controllers in Ember?

Components have sort of condensed three Ember 1.x concepts in one: controller + view + template.

The renderComponent method

Until something equivalent to renderComponent() lands in Ember (“routable components”), we cannot render components directly from any route. We will the shim layer technique, whereby the route renders a template as a proxy to our main component.

In code-speak:

{{!-- app/templates/item.hbs --}}
{{item-display model=model}}

Alternatively, there’s an experimental add-on for routeable components here.

For a working version of our example, check out this Twiddle!

Twiddle URL: https://ember-twiddle.com/5089e12c37900b23c742

Testing and tooling aside, these are the foundations we all need to get solid in Ember. Does this mean that’s all there is?

Of course not!

Upcoming articles will leverage these notions to help you build amazingly powerful applications.

Some in-depth, step-by-step Ember Igniter guides you can continue reading:

And let me know in the comments below what Ember concept you’re struggling with.

Enjoyed this article? Don't miss my next one!

Leave me your e-mail for content that will help you master Ember: