An In-Depth Introduction To Ember.js

Advertisement

With the release of Ember.js 1.0, it’s just about time to consider giving it a try. This article aims to introduce Ember.js to newcomers who want to learn about this framework.

Users often say that the learning curve is steep, but once you’ve overcome the difficulties, then Ember.js is tremendous. This happened to me as well. While the official guides1 are more accurate and up to date than ever (for real!), this post is my attempt to make things even smoother for beginners.

First, we will clarify the main concepts of the framework. Next, we’ll go in depth with a step-by-step tutorial that teaches you how to build a simple Web app with Ember.js and Ember-Data, which is Ember’s data storage layer. Then, we will see how views and components help with handling user interactions. Finally, we will dig a little more into Ember-Data and template precompiling.

An In-Depth Introduction To Ember.js
Ember’s famous little mascot, Tomster. (Image credits2)

The unstyled demo below will help you follow each step of the tutorial. The enhanced demo is basically the same but with a lot more CSS and animations and a fully responsive UX when displayed on small screens.

Unstyled demo3 Source code4 Enhanced demo5

Table of Contents

Definitions Of Main Concepts

The diagram below illustrates how routes, controllers, views, templates and models interact with each other.

ember-sketch45

Let’s define these concepts. And if you’d like to learn more, check the relevant section of the official guides:

Models

Suppose our application handles a collection of users. Well, those users and their informations would be the model. Think of them as the database data. Models may be retrieved and updated by implementing AJAX callbacks inside your routes, or you can rely on Ember-Data (a data-storage abstraction layer) to greatly simplify the retrieval, updating and persistence of models over a REST API.

The Router

There is the Router, and then there are routes. The Router is just a synopsis of all of your routes. Routes are the URL representations of your application’s objects (for example, a route’s posts will render a collections of posts). The goal of routes is to query the model, from their model hook, to make it available in the controller and in the template. Routes can also be used to set properties in controllers, to execute events and actions, and to connect a particular template to a particular controller. Last but not least, the model hook can return promises so that you can implement a LoadingRoute, which will wait for the model to resolve asynchronously over the network.

Controllers

At first, a controller gets a model from a route. Then, it makes the bridge between the model and the view or template. Let’s say you need a convenient method or function for switching between editing mode to normal mode. A method such as goIntoEditMode() and closeEditMode() would be perfect, and that’s exactly what controllers can be used for.

Controllers are auto-generated by Ember.js if you don’t declare them. For example, you can create a user template with a UserRoute; and, if you don’t create a UserController (because you have nothing special to do with it), then Ember.js will generate one for you internally (in memory). The Ember Inspector53 extension for Chrome can help you track those magic controllers.

Views

Views represent particular parts of your application (the visual parts that the user can see in the browser). A View is associated with a Controller, a Handlebars template and a Route. The difference between views and templates can be tricky. You will find yourself dealing with views when you want to handle events or handle some custom interactions that are impossible to manage from templates. They have a very convenient didInsertElement hook, through which you can play with jQuery very easily. Furthermore, they become extremely useful when you need to build reusable views, such as modals, popovers, date-pickers and autocomplete fields.

Components

A Component is a completely isolated View that has no access to the surrounding context. It’s a great way to build reusable components for your apps. A Twitter Button54, a custom select box55 and those reusable charts56 are all great examples of components. In fact, they’re such a great idea that the W3C is actually working with the Ember team on a custom element57 specification.

Templates

Simply put, a template is the view’s HTML markup. It prints the model data and automatically updates itself when the model changes. Ember.js uses Handlebars58, a lightweight templating engine also maintained by the Ember team. It has the usual templating logic, like if and else, loops and formatting helpers, that kind of stuff. Templates may be precompiled (if you want to cleanly organize them as separate .hbs or .handlebars files) or directly written in <script type="text/x-handlebars"></script> tags in your HTML page. Jump to the section on templates precompiling59 to dig into the subject.

Helpers

Handlebars helpers are functions that modify data before it is rendered on the screen — for example, to format dates better than Mon Jul 29 2013 13:37:39 GMT+0200 (CEST). In your template, the date could be written as {{date}}. Let’s say you have a formatDate helper (which converts dates into something more elegant, like “One month ago” or “29 July 2013”). In this case, you could use it like so: {{formatDate date}}.

Components? Helpers? Views? HELP!

The Ember.js forum has an answer60 and StackOverflow has a response61 that should alleviate your headache.

Let’s Build An App

In this section, we’ll build a real app, a simple interface for managing a group of users (a CRUD62 app). Here’s what we’ll do:

  • look at the architecture we’re aiming for;
  • get you started with the dependencies, files structure, etc.;
  • set up the model with Ember-Data’s FixtureAdapter;
  • see how routes, controllers, views and templates interact with each other;
  • finally, replace the FixtureAdapter with the LSAdapter to persist data in the browser’s local storage.

Sketch Our App

We need a basic view to render a group of users (see 1 below). We need a single-user view to see its data (2). We need to be able to edit and delete a given user’s data (3). Finally, we need a way to create a new user; for this, we will reuse the edit form.

app-sketch63

Ember.js strongly relies on naming conventions. So, if you want the page /foo in your app, you will have the following:

  • a foo template,
  • a FooRoute,
  • a FooController,
  • and a FooView.

Learn more about Ember’s naming conventions64 in the guides.

What You’ll Need to Get Started

You will need:

  • jQuery,
  • Ember.js itself (obviously),
  • Handlebars (i.e. Ember’s templating engine),
  • Ember-Data (i.e. Ember’s data-persistence abstraction layer).
/* /index.html
*/
 …
 <script src="//code.jquery.com/jquery-2.0.3.min.js"></script>
 <script src="//builds.emberjs.com/handlebars-1.0.0.js"></script>
 <script src="//builds.emberjs.com/tags/v1.1.2/ember.js"></script>
 <script src="//builds.emberjs.com/tags/v1.0.0-beta.3/ember-data.js"></script>
 <script>
   // your code
 </script>
</body>
</html>

Ember’s website has a builds section65, where you can find all of the links for Ember.js and Ember-Data. Currently, Handlebars is not there; you will find it on the official Handlebars66 website.

Once we have loaded the required dependencies, we can get started building our app. First, we create a file named app.js, and then we initialize Ember:

/* /app.js
*/
window.App = Ember.Application.create();

Just to be sure everything is OK, you should see Ember’s debugging logs in the browser’s console.

console-log-1

Our Files Directory Structure

There’s not much of a convention on how to organize files and folders. The Ember App Kit67 (a Grunt-based environment to scaffold Ember apps) provides a kind of standard for this because it is maintained by the Ember team. Even simpler, you could put everything in a single app.js file. In the end, it’s really up to you.

For this tutorial, we will simply put controllers in a controllers folder, views in a views folder and so on.

components/
controllers/
helpers/
models/
routes/
templates/
views/
app.js
router.js
store.js

Precompile Templates or Not?

There are two ways to declare templates. The easiest way is to add special script tags to your index.html file.

<script type="text/x-handlebars" id="templatename">
  <div>I'm a template</div>
</script>

Each time you need a template, you’d add another script tag for it. It’s fast and easy but can become a real mess if you have too many templates.

The other way is to create an .hbs (or .handlebars) file for each of your templates. This is called “template precompiling,” and a complete section68 is dedicated to it later in this article.

Our unstyled demo69 uses <script type="text/x-handlebars"> tags, and all of the templates for our enhanced demo70 are stored in .hbs files, which are precompiled with a Grunt13171 task. This way, you can compare the two techniques.

Set Up the Model With Ember-Data’s FixtureAdapter

Ember-Data is a library that lets you retrieve records from a server, hold them in a Store, update them in the browser and, finally, save them back to the server. The Store can be configured with various adapters (for example, the RESTAdapter interacts with a JSON API, and the LSAdapter persists your data in the browser’s local storage). An entire section72 is dedicated to Ember-Data later in this article.

Here, we are going to use the FixtureAdapter. So, let’s instantiate it:

/* /store.js
*/
App.ApplicationAdapter = DS.FixtureAdapter;

In previous versions of Ember, you had to subclass the DS.Store. We don’t need to do that anymore to instantiate adapters.

The FixtureAdapter is a great way to start with Ember.js and Ember-Data. It lets you work with sample data in the development stage. At the end, we will switch to the LocalStorage adapter73 (or LSAdapter).

Let’s define our model. A user would have a name, an email address, a short bio, an avatarUrl and a creationDate.

/* /models/user.js
*/
App.User = DS.Model.extend({
  name         : DS.attr(),
  email        : DS.attr(),
  bio          : DS.attr(),
  avatarUrl    : DS.attr(),
  creationDate : DS.attr()
});

Now, let’s feed our Store with the sample data. Feel free to add as many users as you need:

/* /models/user.js
*/
App.User.FIXTURES = [{
  id: 1,
  name: 'Sponge Bob',
  email: 'bob@sponge.com',
  bio: 'Lorem ispum dolor sit amet in voluptate fugiat nulla pariatur.',
  avatarUrl: 'http://jkneb.github.io/ember-crud/assets/images/avatars/sb.jpg',
  creationDate: 'Mon, 26 Aug 2013 20:23:43 GMT'
}, {
  id: 2,
  name: 'John David',
  email: 'john@david.com',
  bio: 'Lorem ispum dolor sit amet in voluptate fugiat nulla pariatur.',
  avatarUrl: 'http://jkneb.github.io/ember-crud/assets/images/avatars/jk.jpg',
  creationDate: 'Fri, 07 Aug 2013 10:10:10 GMT'
}
…
];

Learn more about models in the documentation74.

Instantiate the Router

Let’s define our Router with the routes we want (based on the diagram we made earlier75).

/* /router.js
*/
App.Router.map(function(){
  this.resource('users', function(){
    this.resource('user', { path:'/:user_id' }, function(){
      this.route('edit');
    });
    this.route('create');
  });
});

This Router will generate exactly this:

URL Route Name Controller Route Template
N/A N/A ApplicationController ApplicationRoute application
/ index IndexController IndexRoute index
N/A users UsersController UsersRoute users
/users users.index UsersIndexController UsersIndexRoute users/index
N/A user UserController UserRoute user
/users/:user_id user.index UserIndexController UserIndexRoute user/index
/users/:user_id/edit user.edit UserEditController UserEditRoute user/edit
/users/create users.create UsersCreateController UsersCreateRoute users/create

The :user_id part is called a dynamic segment because the corresponding user ID will be injected into the URL. So, it will look like /users/3/edit, where 3 is the user with the ID of 3.

You can define either a route or a resource. Keep in mind that a resource is a group of routes and that it allows routes to be nested.

A resource also resets the nested naming convention to the last resource name, which means that, instead of having UsersUserEditRoute, you would have UserEditRoute. In other words, in case this confuses you, if you have a resource nested inside another resource, then your files name would be:

  • UserEditRoute instead of UsersUserEditRoute;
  • UserEditControler instead of UsersUserEditController;
  • UserEditView instead of UsersUserEditView;
  • for templates, user/edit instead of users/user/edit.

Learn more about how to define routes76 in the guides.

The Application Template

Each Ember.js app needs an Application template, with an {{outlet}} tag that holds all other templates.

/* /templates/application.hbs
*/
<div class="main">
  <h1>Hello World</h1>
  {{outlet}}
</div>

If you’ve decided to follow this tutorial without precompiling templates, here’s what your index.html should look like:

/* /index.html
*/
  …
  <script type="text/x-handlebars" id="application">
    <div class="main">
      <h1>Hello World</h1>
      {{outlet}}
    </div>
  </script>

  <script src="dependencies.js"></script>
  <script src="your-app.js"></script>
</body>
</html>

The Users Route

This route deals with our group of users. Remember we saw earlier77, in the definitions, that a route is responsible for querying the model. Well, routes have a model hook through which you can perform AJAX requests (for retrieving your models, if you don’t use Ember-Data) or for querying your Store (if you do use Ember-Data). If you’re interested in retrieving models without Ember-Data, you can jump to the section78 in which I briefly explain how to do it.

Now, let’s create our UsersRoute:

/* /routes/usersRoute.js
*/
App.UsersRoute = Ember.Route.extend({
  model: function(){
    return this.store.find('user');
  }
});

Learn more about how to specify the routes model hook79 in the guides.

If you visit your app at the URL http://localhost/#/users, nothing will happen, because we need a users template. Here it is:

/* /templates/users.hbs
*/
<ul class="users-listing">
  {{#each user in controller}}
    <li>{{user.name}}</li>
  {{else}}
    <li>no users… :-(</li>
  {{/each}}
</ul>

The each loop iterates over the users collection; here, controller equals UsersController. Notice that the {{#each}} loop has an {{else}} statement; so, if the model is empty, then no users… :-( will be printed.

Because we’ve followed Ember’s naming conventions, we can omit the declaration of the UsersController. Ember will guess that we are dealing with a collection because we’ve used the plural of “user.”

Object vs. Array Controller

An ObjectController deals with a single object, and an ArrayController deals with multiple objects (such as a collection). We just saw that, in our case, we don’t need to declare the ArrayController. But for the purpose of this tutorial, let’s declare it, so that we can set some sorting properties on it:

/* /controllers/usersController.js
*/
App.UsersController = Ember.ArrayController.extend({
  sortProperties: ['name'],
  sortAscending: true // false = descending
});

Here, we’ve simply sorted our users alphabetically. Learn more about controllers in the guides80.

Displaying the Number of Users

Let’s use UsersController to create our first computed property81. This will display the number of users, so that we can see changes when adding or deleting users.

In the template, we just need something as simple as this:

/* /templates/users.hbs
*/
…
<div>Users: {{usersCount}}</div>
…

In UsersController, let’s declare the usersCount property — but not like a regular property, because this one will be a function that returns the model’s length.

/* /controllers/usersController.js
*/
App.UsersController = Em.ArrayController.extend({
  …
  usersCount: function(){
    return this.get('model.length');
  }.property('@each')
});

Basically, usersCount takes the .property('@each') method, which tells Ember.js that this function is in fact a property that is watching for any changes to one of the models in the collection (i.e. the users). Later, we will see usersCount incrementing and decrementing as we create and delete users.

Computed Properties

Computed properties are powerful. They let you declare functions as properties. Let’s see how they work.

App.Person = Ember.Object.extend({
  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"

In this example, the Person object has two static properties, which are firstName and lastName. It also has a fullName computed property, which concatenates a full name by retrieving the value of the two static properties. Note that the .property('firstName', 'lastName') method tells the function to re-execute if either firsName or lastName changes.

Properties (whether static or computed) are retrieved with .get('property') and can be set with .set('property', newValue).

If you find yourself setting multiple properties consecutively, a better way to do it is with one single .setProperties({}), rather than with multiple instances of .set(). So, instead of doing this…

this.set('propertyA', 'valueA');
this.set('propertyB', valueB);
this.set('propertyC', 0);
this.set('propertyD', false);

… you would do this:

this.setProperties({
  'propertyA': 'valueA',
  'propertyB': valueB,
  'propertyC': 0,
  'propertyD': false
});

The documentation has so much more information about how to bind data with computed properties82, observers83 and bindings84.

Redirecting From the Index Page

If you go to the home page of your app (http://localhost/), you might be asking yourself why nothing is happening. That’s because you are viewing the index page, and we don’t have an index template. Let’s add one, then. We’ll call it index.hbs.

Ember.js will notice that you are creating the index template for IndexRoute; so, no need to tell it anything else about the index in the Router. This is called an initial route. Three of them are available: ApplicationRoute, IndexRoute and LoadingRoute. Learn more about them in the guides85.

Now, let’s add a link to the user’s page with the {{#link-to}}…{{/link-to}} block helper. Why a block helper? Because you’re able to write text between the opening and closing tags, as if it were a real custom HTML element.

/* /templates/index.hbs
*/
{{#link-to "users"}} Go to the users page {{/link-to}}

This takes the route’s name that you want to link to as the first argument (the second optional argument is a model). Under the hood, it’s just a regular <a> element, although Ember also handles for us the active class name when reaching the matching route. Those link-to’s are perfect for navigation menus. Learn more about them in the guides86.

Another approach would be to tell IndexRoute to redirect to UsersRoute. Again, pretty easy:

/* /routes/indexRoute.js
*/
App.IndexRoute = Ember.Route.extend({
  redirect: function(){
    this.transitionTo('users');
  }
});

Now, when you visit the home page, you will immediately be redirected to the /#/users URL.

Single User Route

Before getting our hands dirty with building the dynamic segment, we need a way to link to each user from the users template. Let’s use the {{#link-to}} block helper inside the user’s each loop.

/* /templates/users.hbs
*/
…
{{#each user in controller}}
  <li>
    {{#link-to "user" user}}
      {{user.name}}
    {{/link-to}}
  </li>
{{/each}}

The second argument of link-to is the model that will be passed to UserRoute.

OK, let’s get back to our single user template. It looks like this:

/* /templates/user.hbs
*/
<div class="user-profile">
  <img {{bind-attr src="avatarUrl"}} alt="User's avatar" />
  <h2>{{name}}</h2>
  <span>{{email}}</span>
  <p>{{bio}}</p>
  <span>Created {{creationDate}}</span>
</div>

Note that you can’t use <img src="{{avatarUrl}}">, because data inside attributes are bound with the bind-attr helper. For instance, you could do something like <img {{bind-attr height="imgHeight}}"/>, where imgHeight is a computed property in the current controller.

You’ll find all you need to know about binding attributes87 and class names88 in the guides.

So far, so good. But nothing happens when you click on the user’s links, because we told the Router that we want UserRoute to be nested in UsersRoute. So, we need an {{outlet}} in which to render the user template.

/* /templates/users.hbs
*/
…
{{#each user in controller}}
…
{{/each}}

{{outlet}}

An {{outlet}} is like a dynamic placeholder into which other templates can be injected when {{#link-to}} tags are clicked. It allows for views to be nested.

Now, you should be able to view the user template injected in the page when visiting the page at the URL /#/users/1.

Hey, wait a minute! We have declared neither UserRoute nor UserController, but it’s still working! Why is that? Well, UserRoute is the singular of UsersRoute, so Ember has generated the route and the controller for us (in memory). Thank goodness for naming conventions!

For the sake of consistency, let’s declare them anyway, so that we can see how they look:

/* /routes/userRoute.js
*/
App.UserRoute = Ember.Route.extend({
  model: function(params) {
    return this.store.find('user', params.user_id);
  }
});
/* /controllers/userController.js
*/
App.UserController = Ember.ObjectController.extend();

Learn more about dynamic segments89 in the guides.

Edit a User

Moving on to the edit user form nested in the single user, the template looks like this:

/* /templates/user/edit.hbs
*/
<div class="user-edit">
  <label>Choose user avatar</label>
  {{input value=avatarUrl}}

  <label>User name</label>
  {{input value=name}}

  <label>User email</label>
  {{input value=email}}

  <label>User short bio</label>
  {{textarea value=bio}}
</div>

Let’s talk about those {{input}}90 and {{textarea}}91 tags. This form’s goal is to enable us to edit the user’s data, and these custom input tags take the model’s properties as parameters to enable data-binding.

Note that it’s value=model, without the " ". The {{input}} helper is a shorthand for {{Ember.TextField}}. Ember.js has those built-in views92 especially for form elements.

If you visit your app at the URL /#/users/1/edit, nothing will happen, because, again, we need an {{outlet}} to nest the edit template into the single user template.

/* /templates/user.hbs
*/
…
{{outlet}}

Now, the template is correctly injected in the page. But the fields are still empty, because we need to tell the route which model to use.

/* /routes/userEditRoute.js
*/
App.UserEditRoute = Ember.Route.extend({
  model: function(){
    return this.modelFor('user');
  }
});

The modelFor93 method lets you use the model of another route. Here, we’ve told UserEditRoute to get the model of UserRoute. The fields are now correctly populated with the model data. Try to edit them — you will see the changes occur in the parent templates as well!

Our First Action

OK, now we need a button to click that redirects us from UserRoute to UserEditRoute.

/* /templates/user.hbs
*/
<div class="user-profile">
  <button {{action "edit"}}>Edit</button>
  …

We just added a simple button that triggers our first {{action}}. Actions are events that trigger associated methods in their current controller. If no method is found in the controller, then the action bubbles up through routes until it matches something. This is explained well in the guides94.

In other words, if we click on the button, then it will trigger the edit action found in the controller. So, let’s add it to UserController:

/* /controllers/userController.js
*/
App.UserController = Ember.ObjectController.extend({
  actions: {
    edit: function(){
      this.transitionToRoute('user.edit');
    }
  }
});

Actions, whether in controllers or in routes, are stored in an actions hash. But this is not the case for default actions, such as click, doubleClick, mouseLeave and dragStart. The Ember.js website has a complete list95.

Here, basically, our edit action says, “Go to the user.edit route.” That’s pretty much it.

TransitionTo or TransitionToRoute?

On a side note, transitioning from routes is slightly different from transitioning from controllers:

// from a route
this.transitionTo('your.route')
// from a controller
this.transitionToRoute('your.route')

Saving User Modifications

Let’s see how to save modifications after a user’s data has been edited. By saving, we mean persisting the changes. With Ember-Data, this means telling Store to save() the new record of the modified user. The Store will then tell the adapter to perform an AJAX PUT request (if our adapter is the RESTAdapter).

From our application’s point of view, this would be an “OK” button that saves modifications and then transitions to the parent route. Again, we’ll use an {{action}}.

/* /templates/user/edit.hbs
*/
<button {{action "save"}}> ok </button>
/* /controllers/userEditController.js
*/
App.UserEditController = Ember.ObjectController.extend({
  actions: {
    save: function(){
      var user = this.get('model');
      // this will tell Ember-Data to save/persist the new record
      user.save();
      // then transition to the current user
      this.transitionToRoute('user', user);
    }
  }
});

Our edit mode is working well. Now, let’s see how to delete a user.

Delete a User

We can add a delete button beside the edit button in the user template — again, with a delete {{action}}, this time defined in UserController.

/* /templates/user.hbs
*/
<button {{action "delete"}}>Delete</button>
/* /controllers/userController.js
*/
…
actions: {
  delete: function(){
    // this tells Ember-Data to delete the current user
    this.get('model').deleteRecord();
    this.get('model').save();
    // then transition to the users route
    this.transitionToRoute('users');
  }
}

Now, when you click on the “Delete” button, the user is instantly trashed. A bit rough. We should add a confirm state, something like “Are you sure?” with “Yes” and “No” buttons. To do this, we need to change {{action "delete"}} to make it show confirm-box instead of immediately deleting the user. Then, we obviously need to put confirm-box in the user template.

/* /templates/user.hbs
*/
{{#if deleteMode}}
<div class="confirm-box">
  <h4>Really?</h4>
  <button {{action "confirmDelete"}}> yes </button>
  <button {{action "cancelDelete"}}> no </button>
</div>
{{/if}}

We’ve just written our first Handlebars {{if}} statement. It prints div.confirm-box only if the deleteMode property is true. We need to define this deleteMode in the current controller and then change the delete action to make it toggle deleteMode’s value to true or false. Now, our UserController looks like this:

/* /controllers/userController.js
*/
App.UserController = Ember.ObjectController.extend({
  // the deleteMode property is false by default
  deleteMode: false,

  actions: {
    delete: function(){
      // our delete method now only toggles deleteMode's value
      this.toggleProperty('deleteMode');
    },
    cancelDelete: function(){
      // set deleteMode back to false
      this.set('deleteMode', false);
    },
    confirmDelete: function(){
      // this tells Ember-Data to delete the current user
      this.get('model').deleteRecord();
      this.get('model').save();
      // and then go to the users route
      this.transitionToRoute('users');
      // set deleteMode back to false
      this.set('deleteMode', false);
    },
    // the edit method remains the same
    edit: function(){
      this.transitionToRoute('user.edit');
    }
  }
});

Deletion now works perfectly with the “Yes” and “No” buttons. Awesome! Finally, the last thing to build is the create route.

Create a User

To create a user, let’s do something fun: Let’s reuse the edit template, because the create form will be exactly the same as the edit user form. First, we declare the create route, which will return an empty object in its model hook:

/* /routes/usersCreateRoute.js
*/
App.UsersCreateRoute = Ember.Route.extend({
  model: function(){
    // the model for this route is a new empty Ember.Object
    return Em.Object.create({});
  },

  // in this case (the create route), we can reuse the user/edit template
  // associated with the usersCreateController
  renderTemplate: function(){
    this.render('user.edit', {
      controller: 'usersCreate'
    });
  }
});

Note the renderTemplate method; it enables us to associate a particular template with a route. Here, we’re telling UsersCreateRoute to use the user and edit template with UsersCreateController. Learn more about renderTemplate in the guides96.

Now, let’s define another save action, but this time in UsersCreateController. (Remember that an action first tries to match a corresponding method in the current controller.)

/* /controllers/usersCreateController.js
*/
App.UsersCreateController = Ember.ObjectController.extend({
  actions: {
    save: function(){
      // just before saving, we set the creationDate
      this.get('model').set('creationDate', new Date());

      // create a record and save it to the store
      var newUser = this.store.createRecord('user', this.get('model'));
      newUser.save();

      // redirects to the user itself
      this.transitionToRoute('user', newUser);
    }
  }
});

Finally, let’s add the {{#link-to}} helper in the users templates, so that we can access the creation form:

/* /templates/users.hbs
*/
{{#link-to "users.create" class="create-btn"}} Add user {{/link-to}}
…

That’s all there is to creating users!

Format Data With Helpers

We’ve already defined97 what helpers are. Now, let’s see how to create one that will format an ugly date into a nice clean formatted one. The Moment.js9998 library is awesome for this purpose.

Grab Moment.js9998 and load it in the page. Then, we’ll define our first helper:

/* /helpers/helpers.js
*/
Ember.Handlebars.helper('formatDate', function(date){
  return moment(date).fromNow();
});

Modify the user template so that it uses the formatDate helper on the {{creationDate}} property:

/* /templates/user.hbs
*/
…
<span>Created {{formatDate creationDate}}</span>
…

That’s it! You should see the dates nicely formatted: “2 days ago,” “One month ago,” etc.

Format Data With Bound Helpers

In this case, our date is static data because it’s not going to change in the future. But if you have data that needs to be updated (for example, a formatted price), then you would have to use a BoundHelper instead of the regular helper.

/* /helpers/helpers.js
*/
Ember.Handlebars.registerBoundHelper('formatDate', function(date){
  return moment(date).fromNow();
});

A bound helper is able to automatically update itself if the data changes. Learn more about bound helpers in the guides100.

Switch to the LocalStorage Adapter

Our app looks to be working fine, so we are ready to switch to the real thing. We could enable the RESTAdapter, but then we would need a REST server on which we could perform GET, PUT, POST and DELETE requests. Instead, let’s use LSAdapter, a third-party adapter that you can download on GitHub101. Load it in your page (just after Ember-Data), comment out all of the FIXTURE data, and change ApplicationAdapter to DS.LSAdapter:

/* /store.js
*/
App.ApplicationAdapter = DS.LSAdapter;

Now, your users’ data will persist in local storage. That’s all! Seriously, it’s that easy. Just to be sure, open the Developer Tools in your browser and go into the “Resource” panel. In the “Local Storage” tab, you should find an entry for LSAdapter with all of your users’ data.

console-localstorage

Playing With Views

So far, we haven’t declared any views in our simple CRUD, only templates. Why would we care about views? Well, they are powerful for events handling, animations and reusable components.

jQuery and the didInsertElement

How can we use jQuery the way we are used to for Ember.js’ views? Each view and component has a didInsertElement hook, which assures us that the view has indeed been inserted into the DOM. With that, you have secure jQuery access to elements in the page.

App.MyAwesomeComponent = Em.Component.extend({
  didInsertElement: function(){
    // this = the view
    // this.$() = $(the view)
    this.$().on('click', '.child .elem', function(){
      // do stuff with jQuery
    });
  }
});

If you’ve registered jQuery-like events from inside didInsertElement, then you can use willDestroyElement to clean them up after the view has been removed from the DOM, like so:

App.MyAwesomeComponent = Em.Component.extend({
  didInsertElement: function(){
    this.$().on('click', '.child .elem', function(){
      // do stuff with jQuery
    });
  },
  willDestroyElement: function(){
    this.$().off('click');
  }
});

Side Panel Components With className Bindings

The combination of computed property and className binding sounds like a scary technique, but it’s really not that bad. The idea is that we add or remove a CSS class on an element if a property is either true or false. Of course, the CSS class contains a CSS transition.

Suppose we have a hidden div in the DOM. When this div has a class of opened, it slides in. When it has a class of closed, it slides out. A side panel is a perfect example for this, so let’s build one.

Here’s a JS Bin so that you can test the code:

Reusable Ember.js side panels102

Let’s go through each tab in turn:

  • JavaScript tab
    First, we declare our SidePanelComponent with default classNames. Then, classNameBindings is used to test whether isOpen is true or false, so that it returns closed or opened. Finally, component has a toggleSidepanel action that simply toggles the isOpen boolean.
  • HTML tab
    This is the side panel’s markup. Note the {{#side-panel}}…{{/side-panel}} block tags; we can put whatever we want between them, which makes our side panel incredibly reusable. The btn-toggle button calls the toggleSidepanel action located in the component. The {{#if isOpen}} adds some logic by checking the value of the isOpen property.
  • CSS tab
    Here, we are basically putting the side panel off screen. The opened class slides it in, and closed slides it out. The animation is possible because we are listening for translate2D changes (transition:transform .3s ease).

The guides have a lot more examples on how to bind class names from components103 and from inside templates104.

Modals With Layout and Event Bubbling

This technique is way more complicated than the previous one, because a lot of Ember.js features are involved. The idea is to make an event bubble from a view to a route so that we can toggle a property located in a controller somewhere in the app. Also, here we are using a View instead of a Component (remember that, under the hood, a component is an isolated view).

Reusable Ember.js modals105

  • JavaScript tab
    The modalView is the default layout for all of our modals. It has two methods, showModal and hideModal. The showModal method is called with an action that bubbles up, first through controller, then through routes, until it finds a corresponding showModal action. We’ve stored showModal in the highest route possible, the applicationRoute. Its only goal is to set the modalVisible property inside the controller that was passed in the action’s second argument. And yes, creating a property at the same time as we set it is possible.
  • HTML tab
    Each modal has its own template, and we’ve used the convenient {{#view App.ModalView}}…{{/view}} block tags to encapsulate them in modal_layout. The modal’s controllers are not even declared because Ember.js has them in memory. Note that the {{render}} helper takes parameters, which are the template’s name and a generated controller for this template. So, here we are calling a modal01 template and a modal01 controller (auto-generated).
  • CSS tab
    For the purpose of this example, modals need to be present in the DOM. This can feel like a constraint, but the main benefit is the reduced paint cost; otherwise, Ember has to inject and remove them every time we call them. The second benefit is CSS transitions. The shown class applies two transitions: first, the top position (because the modal is off screen by default), then, with a little delay, it transitions the opacity (which also has a reduced106 paint cost107 when transitioning). The hidden class does the same in reverse. Obviously, you can apply a lot of cool transitions108 to your modals if they stay in the DOM.

The guides have a lot more information about events109, event bubbling110, layouts111 and the {{render}}112 helper tag.

What Is Ember-Data?

Ember-Data is in beta as of the time of writing, so please use it with caution.

It is a library that lets you retrieve records from a server, hold them in a store, update them in the browser and, finally, save them back to the server. The store may be configured with various adapters, depending on your back end. Here’s a diagram of Ember-Data’s architecture.

ember-data-sketch

The Store

The store holds data loaded from the server (i.e. records). Routes and controllers can query the store for records. If a given record is called for the first time, then the store tells the adapter to load it over the network. Then, the store caches it for the next time you ask for it.

Adapters

The application queries the store, and the adapter queries the back end. Each adapter is made for a particular back end. For example, the RESTAdapter deals with JSON APIs, and LSAdapter deals with local storage.

The idea behind Ember-Data is that, if you have to change the back end, then you simply plug another adapter, without having to touch a single line of your application’s code.

  • FixtureAdapter
    FixtureAdapter is perfect for testing Ember and Ember-Data. Fixtures are just sample data that you can work with until your app reaches the production phase. We went over how to configure it in an earlier part of this article113.
  • RESTAdapter
    RESTAdapter is the default adapter in Ember-Data. It lets you perform GET, PUT, POST and DELETE requests over a REST API. It also requires some specific JSON conventions114 in return. Enabling RESTAdapter looks like this:

    App.ApplicationAdapter = DS.RESTAdapter.extend({
      host: 'https://your.api.com'
    });
    

    There’s a lot more to discover about RESTAdapter in the guides115.

  • Custom adapter
    You could use something other than the two default adapters (FixtureAdapter and RESTAdapter). A bunch of them are on GitHub116. For instance, there’s the LocalStorage Adapter117, which is demonstrated in the guides’ sample Todos118 app and is also the one I use in the demo119.

What About Not Using Ember-Data?

In this article, I’ve chosen to cover Ember-Data because it’s almost stable and is probably one of the coolest thing happening these days in the JavaScript world. But perhaps you’re wondering whether getting rid of it is possible. The answer is yes! In fact, using Ember.js without Ember-Data is pretty easy.

There are two ways to do it.

You could use another library for your model’s retrieval and persistence. Ember-Model120Ember-Resource121Ember-Restless122 and the recent EPF123 are good alternatives. EmberWatch has written a great little article that sums up “Alternatives to Ember Data124.”

The other way would be to not rely on a library, in which case you would have to implement methods to retrieve models with AJAX calls. “Ember Without Ember Data125,” by Robin Ward (the guy behind Discourse126), is a great read. “Getting Into Ember.js, Part 3127” by Rey Bango on Nettuts+ deals specifically with models.

For instance, here’s a static method with reopenClass on a model:

/* /models/user.js
*/
// our own findStuff method inside the User model
App.User.reopenClass({
  findStuff: function(){
    // use regular AJAX / Promises calls
    return $.getJSON("http://your.api.com/api").then(function(response) {
      var users = [];
      // creates new Ember objects and store them into the users Array
      response.users.forEach(function(user){
        users.push( App.User.create(user) );
      });
      // finally returns the array full of Ember Objects
      return users;
    });
  }
});

You would use your findStuff method in your routes’ model hook:

/* /routes/usersRoute.js
*/
App.UsersRoute = Em.Route.extend({
  model: function(){
    return App.User.findStuff();
  }
});

What Is Handlebars Template Precompiling?

Basically, template precompiling entails grabbing all Handlebars templates, transposing them into JavaScript strings, and then storing them in Ember.TEMPLATES. It also entails an additional JavaScript file to load in your page, which will contain the JavaScript-compiled versions of all of your Handlebars templates.

For very simple apps, precompiling can be avoided. But if you have too many <script type="text/x-handlebars"> templates in your main HTML file, then precompiling will help to organize your code.

Furthermore, precompiling your templates will enable you to use the runtime version of Handlebars, which is lighter than the regular one. You can find both the runtime and standard versions on the Handlebars website128.

Template Naming Conventions

Partials129 have to begin with a _. So, you will have to declare a _yourpartial.hbs file or, if you don’t precompile your templates, a <script type="text/x-handlebars" id="_yourpartial"> tag.

Components130 have to begin with components/. So, you will have to store them in a components/ folder or, if you don’t precompile templates, declare a <script type="text/x-handlebars" id="components/your-component"> tag. Component names are hyphenated.

Otherwise, views have a templateName property in which you can specify which template to associate with the view. Take this declaration of a template:

<script type="text/x-handlebars" id="folder/some-template">
  Some template
</script>

You can associate it with a particular view:

App.SomeView = Em.View.extend({
  templateName: 'folder/some-template'
});

Precompiling With Grunt

If you use Grunt13171, then you probably use it for other building-related tasks (concatenation, compression, that kind of stuff), in which case you should be familiar with the package.json file, which comes with Node.js and Node Packaged Modules. I’ll assume you are already familiar with Grunt.

As of the time of writing, two plugins are available for Grunt to transpose your .hbs files to a templates.js file: grunt-ember-handlebars132 and grunt-ember-templates133. The latter seems a bit more up to date than the former.

I’ve made a Gist for each of them, in case you need help with configuration:

Once it’s configured, you should be able to run grunt in a command-line editor, which should produce the templates.js file. Load it into index.html (after ember.js), and then go into the browser’s console and type Em.TEMPLATES. You should see a hash containing all of the compiled templates.

Be aware that Ember.js doesn’t need the template file’s complete path, nor the file’s extension. In other words, the template’s name should be users/create, not /assets/js/templates/users/create.hbs.

Both plugins have options to handle this. Simply refer to the respective guide, or look at the Gists linked to above. You should end up with something like this:

console-templates

And this is exactly what we need to make everything work as intended. It’s all you need to know about precompiling with Grunt.

Precompiling With Rails

Precompiling with Rails is surely the easiest way to do it. The Ember-Rails gem136 handles pretty much everything for you. It almost works out of the box. Carefully follow the installation instructions in the readme file on GitHub, and you should be all good. Right now, in my humble opinion, Rails has the best Ember and Handlebars integration available.

Tools, Tips And Resources

Chrome Ember Extension

Ember Extension137 is a very convenient Chrome extension. Once installed, an “Ember” tab will appear near the “Console” tab. Then, you can navigate through controllers, routes and views. And the “Data” tab will greatly help you to explore your records if you are using Ember-Data.

console-ember-extension138
Exploring your app’s objects has never been so easy.

Ember App Kit

Maintained by the Ember team, the Ember App Kit139 lets you easily scaffold Ember.js apps. It contains Grunt140 for compiling assets, JSHint141, QUnit142, the Kharma143 test runner, Bower144 and ES6 Modules145 support.

Ember Tools

This GitHub project, Ember Tools146, is a basic command-line interface for creating and scaffolding Ember apps. Take a minute to watch the animated GIF in the readme file, and you’ll see why it’s so cool.

Development and Minified Version

Always use the development build when developing because it contains a lot of comments, a unit-testing package and a ton of helpful error messages, all of which has been removed in the minified build. Find links to both in the builds section of the Ember.js website147.

Debugging Tips

Ember.js usually gives you cool human-readable errors in the browser’s console (remember to use the development version). Sometimes, though, figuring out what’s going on is tricky. Some convenient methods are {{log something}} and {{controller}}, which helpfully prints the current controller for the template to which you’ve added this helper.

Or you could log each Router transition like so:

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

The guides have an exhaustive list148 of these handy little methods.

Properly Comment Your Handlebars

This one can be frustrating. Never ever comment a Handlebars tag with a regular HTML comment. If you do, you’ll completely break the app, without getting a clue about what’s happening.

// never do this
<!-- {{foo}} -->

// instead do this
{{!foo}}

Conclusion

I hope this long article has given you a better understanding of this awesome framework. But the truth is, we’ve only scratched the surface. There’s so much more to cover. For instance, we have the router and its asynchronous nature, which resolves model requests with promises (so that you can easily implement loading spinners). There is also the object model, with its class and instances inheritance, mixins, observers, filters, macros, collectionViews, components, dependencies managed between controllers, and testing package. And so much more!

Obviously, I couldn’t cover everything. Fortunately, the guides will take you through all of these topics very well.

Happy Ember.js coding, folks!

Resources

Acknowledgments

Huge thanks to Mathieu Breton154 and Philippe Castelli155, who both taught me everything they know about Ember.js while I was learning it. Also, a big thank you to Tom Dale156, who helped me to revise this very long article.

(al, il)

↑ Back to topShare on Twitter

Julien is a self-taught interface designer and a front-end developer living in Paris where he is freelancing for some of the biggest French companies. When he is not bringing his 10 years of Web experience to his clients, you'll probably find him writing on his blog or trying to create cool CSS stuff on Codepen.

  1. 1

    It seems a great piece of text and I would gladly giving it a deeper reading. But I have to admit that after the 10nth paragraph still I didn’t quite get what exactly Ember.js lets you build.

    What’s good for? Websites? Apps? CMS? Instant coffee?

    I scrolled down quickly looking for that detail, and I reached the end of the page. Still very confused. Period :)

    -2
    • 2

      Ember is for single page web apps (SPA). Sorry, I should’ve made it clearer from the beginning.

      1
      • 3

        I agree that some explanation of WHAT Ember is and WHY I should want to use it is needed. This article is written as a code introduction to Ember, but neglects to give any sort of conceptual introduction and I guess assumes everyone already knows what Ember is. Which sort of misses the point of an “introduction.”

        0
        • 4

          Will, I disagree. If you want to know what ember is and why you should use it you should check out emberjs.org. I would think its rare that people go looking for tutorials for something when they have no idea what it is. This is an introduction and from what I can see a very good one. Thanks

          6
      • 5

        First of all really nice tutorial. Julien, you’ve mentioned “Ember is for single page web apps” but at the bottom of the http://emberjs.com/ in the “ROUTING” section there is: “Ember.js makes it downright simple to create sophisticated, multi-page JavaScript applications with great URL support”.
        Can you please comment on it?

        0
        • 6

          Hi Cyril, when you go on a website, at each new page you visit there is a “full page refresh”, because the server has to serve you that page you requested, right?

          Single Page Applications usually have multiple pages but when you access them there’s no “page refresh”. It’s supposed to be almost instantaneous. The Router is responsible for handling all the magic so you can have coherent URLs for each page.

          “Single Page” because all you need is an index.html page where to host your app. It’s a bit like those old Flash websites.

          Hope it helps!

          1
    • 7

      Ember.js is famous Single Page Application framework similar to AngularJs or Meteor. You can create webapps that doesn’t need to refresh your entire page for communicating with it. Apps like Trello are developed like this.

      0
    • 8

      Ulises Ramirez-Roche

      November 7, 2013 10:47 am

      As of a few versions ago, partials no longer have to be prefixed with an underscore for it to work. Try it out. You should also be using the .then() pattern for validations or you may run into some bugs, as Matt mentioned below. I’m true-that’ing this again.

      Carlo, ember is for single page web applications. You could use it as a front-end for your web application, (they would communicate using JSON, for example) or to design a landing page, or even stand-alone widgets and components that can be embedded into any other site. I’m using it to make a music video jukebox and a massively multiplayer real-time boardgame about intergalactic werewolves. Essentially, just think of it as making desktop applications on the web.

      0
    • 9

      Thanks all for the replies!
      Gotta read this article again and for good, now ;)

      0
  2. 10

    I only reviewed it quickly, but I think they should be using the “then” pattern of promises when saving and transitioning.

    So this:

    this.get('model').save();
    this.transitionToRoute('users');

    Should be:

    var controller = this;
    this.get('model').save().then(function(model) {
    controller.transitionToRoute('users');
    }).fail(function(error) {
    //Feedback for the user
    });

    1
    • 11

      Absolutely! I haven’t talk that much about promises. It’s another really powerful feature of the framework. Thanks for mentioning.

      0
    • 12

      Stefan Haslinger

      July 1, 2014 4:22 pm

      Thanks for your comment, I was banging my head against walls already.
      As a Javascript newbee I forgot to preserve the this …

      0
  3. 13

    As someone from the UK, I find the mascot unfortunately reminds me of the “Bo Selecta Bear”!

    0
  4. 17

    Damn that’s a long article, over 7,000 words – could have done with being put into parts, will make some good bedtime reading though!

    0
  5. 18

    This is gold. Period.

    0
  6. 19

    EmberJS is used for quickly developing dynamic, single-page web applications (web pages). Think AngularJS. Once you muddle through the end-to-end process of putting things together, developing with Ember becomes extremely efficient and rewarding.

    Many thanks to the author for writing this great overview.

    0
  7. 20

    Ember is indeed good at quickly developing dynamic, single-page web apps – but they have to be pretty simple too. Once you try anything fancy, you run up against walls that are more or less insurmountable. (My experience is from March 2013; some things may have changed but I doubt the architectural issues have improved.) I tried implementing a straight list-of-lists editor that would be no problem using server-side only tech like JSF or ASP, but the framework would simply not allow it. The documentation was very good, so long as you read it over and over again and memorize every detail. Names had to be absolutely, perfectly spelled and capitalized the Ember way, or Ember would simply do nothing, and it was up to you, the developer, to figure out why. And if you had something similarly named (as you would in a list-of-lists application), Ember would also refuse to handle it, or something else on the page that was working fine before. After a month of struggling with getting Ember to do what I wanted, I switched cold to Backbone+Handlebars and had everything working in about a week. My advice is, if you want to try using Ember on your web application, then you will be better off for the experience and maybe Ember is all you need. But don’t ignore Angular or Backbone, as you may need to jump ship to one of those when you find out that Ember doesn’t work out for you (or more accurately, you don’t work out for Ember).

    0
    • 21

      Ember applications do not need to be simple. Myself and a dozen other developers are currently at the end of an eight-month development process building an enterprise-level web application for a large financial company using Ember for the client-side code. It is, by every definition, a large application backed with a CQ5 backend and a large database of documents that is very usable with lots of interactive content. By using Ember, we wrote a significantly smaller amount of code than we would have using something like Backbone or rolling our own framework specifically for this project.

      Ember is what is referred to as an opinionated framework, much like Rails from the Ruby community, except it’s for the front end. That means you get a lot for free from the framework if you follow the naming conventions setup by the framework. There’s no real magic to it, though I see a lot of people who are disturbed by frameworks that require a strict naming convention. I don’t understand it myself. Really, it makes life so much easier. It doesn’t take long to learn the naming conventions and they’re spelled out in the documentation.

      If Ember isn’t working for you, it’s not Ember’s fault. Some people find the learning curve to be steep and I’ve struggled with other developers trying to get the concepts. It’s really an unlearning process in my opinion. People often are tempted to compare Ember to another framework they’re more familiar with. I think my Rails experience, though minimal, has helped me to grab the core concepts. In my opinion, developing with Ember is more akin to native application development than it would be to just about anything else.

      1
    • 22

      Alice is absolutely right on with this criticism. The whole “list of lists” thing is a real problem. I think ember has a very poor story around nested resources. At least ones that are arbitrarily deep. For example how might one model a deeply nested tree structure in Ember? Think a collapsible tree that models a file system. It is one of the things that really frustrates me about the framework. I have searched and searched. And have yet to find a clear example of nested resources of resources of resources example. That sentence is not a typo. Basically hoping to see a collection of collections of collections, a nested hierarchy at least three levels deep. Or even better n number of levels deep. Would love to see an example. All the examples I have found seem to only go one level deep. I am not convinced it is impossible with Ember. But it is a very real pain point and not very intuitive. It is the one issue that still to this day, almost every day, tempts me to give up on Ember.

      Alice, I think you can partly get around this issue with the strategic use of components but it still takes some effort.

      If someone with more knowledge than I could take stab at fleshing out this example that would be helpful.

      http://emberjs.jsbin.com/OPIBOkE/1/edit

      Other than that there are a lot of things to like about Ember. It does take some getting use to and there are definitely conventions baked into the framework that you are better off not trying to fight too much.

      0
      • 23

        Really, it’s not that complicated to do nested data. Here’s a JSBin of one approach: http://jsbin.com/olEDivI/3/edit

        Part of this approach relies on using a partial (called _node) that references itself. I didn’t see it document. Tried it one day on a whim and it worked.

        I coded this example in a pinch, so take it for what it is. I used a different approach with more complex models that had computed values and what-not. The approach is stilll similar, though.

        1
  8. 24

    So, I think I broke your demo :/

    Added a new user, deleted 2 users, and now clicking between users is finicky.

    0
  9. 25

    Excellent article, great reading!

    I think your users template can even be a little shorter:

    /* /templates/users.hbs
    */
    {{#each}}
      {{name}}
    {{else}}
      no users… :-(
    {{/each}}
    
    0
  10. 26

    Thanks a lot for this write up. It’s a lot simpler than the todos quick start guide. People completely new to Ember might need to read the guides first before reading this.

    0
  11. 28

    This is all great, etc but the one thing I’ve noticed about all “these frameworks” that pop up all over the -beep- place is that they have dependencies… an enormous amount of dependencies in some cases.

    That is unfortunate to be polite about it, why can we not have a framework that stands alone, on it’s own -beep- damn feet huh? On the GitHub page, scroll down and it says install Grunt.

    Like, what the -beep- is Grunt? Never heard of it before, it’s the same -beep- pattern with so many other “time saving, life consuming” frameworks that work with CSS and Javascript, etc.

    -beep- that :(

    -3
    • 29

      The only requirements for Ember is jQuery (not unusual) and Handlebars, which is by the same developer and used for templating. Grunt is used by a great many developers to automate their development workflow. It’s not part of Ember in anyway. Right on the EmberJS.com website homepage is a link to download their files. The starter kit gives you everything you need to get started.

      2
  12. 30

    Hi
    What do you think about AngularJS ?
    AngularJS vs Ember ?
    which is one better ?
    with asp.net mvc , better use Angular or Ember ?
    Thanks .

    0
  13. 32

    This is just like Ruby on Rails, I like it! Thank you for this wonderful article!

    0
  14. 33

    make in-depth angular.js, pleeeeeaaaaaassee

    0
  15. 35

    Awesome
    Well explained: templates, views and components differences.
    Thank you

    0
    • 36

      Well, it’s your pretty basic and very much average computer science isn’t it? It’s called MVC by any other name, you know… separation of concerns, what we developer learn about for development on the serverside, however passed on to the client under another assumed name, terminology, what ever you like to call it.

      It’s old techniques, refurbished, and it’s these techniques that have already been well documented, well discussed before.

      Next!

      -2
  16. 37

    Fantastic article. You could publish this as a mini-book, I’d be your first customer.

    0
  17. 39

    Hi ! Great article.

    Did you ever try Sais.JS ? If yes, what do you think of it ?

    0
  18. 41

    Very good! It is a good intro but it becomes detailed when needed, just perfect! Keep up the good work, I’m interested in models that are big something like 20000 -30000 elements in array. I have some solution for that but I’m want to see cleaner ideas… Another thing is, check out http://emblemjs.com I think it eliminates the HTML boilerplate the comes with handlebars.

    0
  19. 42

    While this sort of in-depth, detailed tutorial article is great, it suffers by omitting information or assuming too much knowledge at certain steps. Anyone simply following along with the directions will quickly become unstuck – as a first hurdle, it doesn’t say how you’re supposed to load all the model and route JavaScript files. Do you include each one as you create them, or does Ember auto-load them, require.js style?

    Looking at the demo site doesn’t help, because the code is completely different from the examples given in the article; you’re using Grunt to concatenate files, and the folder structure is completely different from that shown here. If you want to encourage more people to try using Ember, you need to avoid this sort of annoying issue, as it will just put people off reading any further.

    Either simplify the demo so that it actually matches the article, or cover the additional information in the tutorial. Otherwise it’s just an exercise in frustration that you have assumed your readers will make logical leaps that you have skipped entirely.

    1
    • 43

      Hi Matthew, simply step back a little.

      There are 2 demos, one “enhanced” with Grunt templates precompiling and one “unstyled” so you can follow along with the tutorial steps.

      And for the files loading, you add them by hand or you let your favorite task tool do it for you (that’s why I used Grunt in the “enhanced” demo). The “unstyled” demo has everything in a single <script> tag.

      0
      • 44

        Julien: Yes, I can work all that out – my point was that I shouldn’t have to. The ideal tutorial should “just work” without having to guess what the author meant. For example, you throw in a reference to “dependencies.js” without any explanation – that could easily confuse anyone trying to follow along with the instructions.

        0
        • 45

          Ok I got your point. I’ll focus on this next time.

          0
        • 46

          @Matthew -
          Perhaps you should do some soul searching on structuring single page applications, or more in depth research on using grunt (or other automated task runners) to compile resources. (clearly stated in the article) This is a great tutorial, clear and concise in my opinion. It annoys me that people write such lame remarks when their misconception is only part of their own ignorance. At some point in a written tutorial, you’re going to have to venture off on your own and forge your own path. This is an “Introduction” to Ember, and quite honestly one of the best I’ve found to date. As Julien mentioned (and the article as well), there are two versions. If the “Styled” version is confusing, stick with the “Unstyled” version until it makes more sense to you.

          Rather than complaining, you should thank the author for taking time to write a tutorial to help people like you understand new technologies. Ember has a fairly steep learning curve, and there is no way it can all be covered (nor should it) in a single page beginner tutorial.

          GREAT article Julien, thanks for your work and contributions to the community. =)

          0
  20. 48

    Well, it’s your pretty basic and very much average computer science isn’t it? It’s called MVC by any other name, you know… separation of concerns, what we developer learn about for development on the serverside, however passed on to the client under another assumed name, terminology, what ever you like to call it.

    It’s old techniques, refurbished, and it’s these techniques that have already been well documented, well discussed before.

    -3
  21. 49

    Hi,

    I have developed some Web Application using EmberJs (Client Side JavaScript) and NodeJs(Server Side JavaScript).

    Emberjs is easy, fast and flexible, compare with AngularJs.

    Waiting for Advance level Emberjs article..

    Thanks Julien Knebel :)

    0
  22. 50

    This is the best ember.js tutorial for beginners I’ve read so far. It’s comprehensive and it’s easy to follow. Thanks for writing all these!

    0
  23. 51

    Hey,

    just noticed a small issue.

    In the grunt-ember-templates Gist – https://gist.github.com/jkneb/6599001 — in the package.json file on line 9, you’ve got: “grunt-ember-handlebars”: “*”.

    I think it should be: “grunt-ember-templates”: “*” right?

    0
  24. 53

    Great tutorial, I’m a little confused about the file structure though… At the top of each code snippet it looks like there’s a filename/path in the comments, but in the actual deployed code it looks like everything is in “app.js”. Are those filenames a recommendation, but not the way you wrote it? If we are to break things up in different files like that, do you recommend just putting the script tags in the appropriate order right in the root of the page?

    EDIT: Nevermind, I see where you specify that at

    Just needed some more time to grok it :-)

    0
  25. 54

    Congratulations for the article, was the best I’ve read so far about Ember.

    I have some questions:

    1. When you declare resources in the User model, it is not explicit that this model will have the routes to edit / new / delete / view? Or really need to declare each specific route for each CRUD action?

    2. There is a less bureaucratic way to organize the CRUD templates for the UserEdit / userDelete / userCreate / UserView not separated?

    3. A controller is always connected to a template?

    I look forward to more tutorials Ember! You also take a look at the Sails, I’m quite grateful! =]

    0
  26. 55

    I’m getting the following error:

    Em.Object.create {}

    “Object user has no method ‘_create’”

    0
    • 56

      I’m getting the same error as well, using rails 4 & ember-rails gem to compile, does anyone know the problem?

      0
      • 57

        Hi guys, I’d need way more data to help you out on this. You could maybe open an issue on the project on Github: https://github.com/jkneb/ember-crud/issues

        0
        • 58

          Hi Julien,

          Figured it out bit of a rookie error sorry. The ember-rails gem defaults to ember-data version 0.14, so basically I just had to update the ember-data-version. Now using 1.0.0.beta.3 and its working fine! Thanks for the great tutorial :)

          If this helps anyone else using rails, I did the following to get it running:

          1. Add this to gemfile and run bundle:
          gem "ember-data-source", "~> 1.0.0.beta.3"
          2. Run this in console:
          rails generate ember:install --tag=v1.0.0-beta.3 --ember-data
          3. Ensure if using fixtures, store.js has:

          App.Store = DS.Store.extend({
             adapter: DS.FixtureAdapter
          });
          0
          • 59

            Cool! One thing though. With this version of Ember Data, there’s no need to subclass DS.Store anymore, you should simply instantiate your adapter like this:

            App.ApplicationAdapter = DS.FixtureAdapter;

            0
  27. 60

    One of the reasons frameworks get traction is because websites like SM publish articles like “introduction to framework X”. The need for such articles show that something is missing in either the framework or it’s documentation. For example Backbone does too little so you need articles to show how you can do more with it, while Angular, Ember, Marionette and the like need such articles to explain concepts, flows and conventions.

    I have found that CanJS can do just as much as Ember without the “hidden magic” and I was able to learn it directly from their website. It’s a shame that CanJS is not as famous as Ember or Angular.

    0
  28. 61

    Exactly the article i was looking for…since ages, or the time i discovered Ember.js.

    Thank you very much for such a detailed post.

    0
  29. 62

    Thanks a bunch for doing this. This should be the “official” Getting Started guide.

    Not only does it introduce Ember, it also teaches some of the best practices with regards to dependency management, template pre-compilation, bundling and building for deployment.

    I’ve been scouring the internet looking for a guide to development workflow for building and deploying web apps (yes, I’m new to web development) especially using ember and requirejs. I figured it all out the hard way.

    Only wish, I’d bothered to look into the attached source code sooner than I did.

    I just posted today on stackoverflow asking for suggestion and recommendations and I haven’t received a single answer. This source code organization answers all my questions and clears my doubts.

    Once again. Thanks!

    0
  30. 64

    the article + comments are pure gold, thanks

    0
  31. 65

    I’ve just read an article in the german iX Magazine which is very very similar to your one. It’s quite obvious to me that the author has used ideas from your article. Unfortunatly he didn’t add a reference.

    0
  32. 67

    I appreciate you taking the time to put this tutorial together. It adds a lot in delineating how the constituent parts of the framework really work together in ways that are absent from the EmberJS documentation.

    My only gripe: I tried doing this project without the use of grunt. I wouldn’t assume that everyone here would have a working knowledge of grunt compilation or the Ember architecture for directories. In fact, the majority of my templates were all dropped into the index.html using `type=”text/x-handlebars” data-template-name=”foo”`, which I know isn’t the most efficient way to handle multiple templates, but it’s what I would think is easiest for true beginners to Ember.js and JavaScript frameworks in general.

    I, personally, would have probably just stuck to introducing the concepts and not immediately gone into using .hbs and grunt compilation. It’s more immediately transparent to me what is being rendered when I can look around the index file and see type=”text/x-handlebars” data-template-name=”user/edit”. Just a thought. I’m a true beginner to JavaScript frameworks, and Ember is definitely ambitious for novices like myself, so it helps when we can see the application working in its simplest of states and build up to the optimized, organized, and precompiled versions. I hope that makes sense.

    0
    • 68

      Hi Ben, I totally agree. Actually, I really had a hard time trying to figure out what to put in this article. Almost every resources out there use templates in script tags but every one building something with Ember (I mean for real) will have to deal with precompiling. So that’s why I finally decided to add it in the article.

      0
  33. 69

    Julien – awesome article. I’m 2 weeks into learning Ember (and generally web programming) and I’ve been through 3 different tutorials. This is the best at taking it from conceptual through details in a much clearer way than anything else I’ve followed. Thanks so much for the hard work putting this together.

    0
  34. 71

    If anyone is interested, I finished this tutorial using Ember App Kit. There’s not a ton of differences besides some simple directory and syntax changes. https://github.com/baudoin/users

    0
    • 72

      I’d love to see a version of this using the Ember App-Kit. Any thoughts on making that a reality? It seems like the jump from “include all of these js files willy-nilly” to “building a real, structured app” is where a lot of people get hung up.

      0
  35. 73

    hey great tutorial thanks!

    would be great if you could modify this to be complete with a rest adapter. say with mongodb and expressjs

    0
    • 74

      Absolutely true. The article was already very long so I decided to avoid it.
      However, check out the repo on Github, at the root directory you’ll find a server.js file which is (well it was at the time of writing) working with a REST Express server. And for the Mongo part you can use the default Node.js Mongo driver or maybe Mongoose (which might be a little bit easier for beginners I guess).
      Otherwise, I’m actually working on a more advanced article to follow this one, which will cover all those backend stuff.

      0
  36. 75

    Thank you for the really useful post!

    I have a question about the binding of the user model both in the edit template and the list template. I would preferably not change the user’s data in the list until the ‘ok’ button had been clicked in the edit. But as of now, any changes made in while editing are of course reflected immediately in the list, since they share the model.

    I’m going to have a go at this myself, but thought I’d throw the question to you, just in case you had a quick answer.

    0
    • 76

      Simply take the inputs and wrap them in <form action="save" on="submit"></form> tags, and it should do the trick. Let me know if it’s not working.

      0
      • 77

        Julien, can you please elaborate on this? Why would wrapping elements in a form prevent data binding?

        0
        • 78

          Actually I don’t know how it works.

          What I do know is that by default an {{input}} has an action fired each time you press a letter on your keyboard, so that’s why the model is changing immediately everywhere in the application.

          But when you add a <input type="submit"> button AND a <form {{action "save" on="submit"}}></form> to surround your inputs, then it’s the action of submitting which will update the model.

          Sorry not being very precise :)

          0
          • 79

            I tested this and unfortunately, the changes to the model still happens on every keystroke. It doesn’t wait for the submit action until updating the model.

            0
          • 80

            Dammit you’re right! I was saying crap!

            Then I believe this is more complicated than that because Ember wants to update every instance of the model everywhere he finds one so it’s a bit like trying to go against one of Ember’s main feature.

            Actually, you’d have to maintain a duplicated model (only for the form I mean) and then update the real model when you’ll click the submit button. It’ll work for sure, but it might be annoying to handle. That’s the only solution I can find right now.

            0
          • 81

            Argh! It doesn’t look like I’m able to paste my example.

            0
  37. 82

    when I run createRecord using this.get(‘model) I keep getting the following error:

    “Assertion failed: Cannot clone an Ember.Object that does not implement Ember.Copyable”

    This can be avoided if I just map out properties individually, although it’s not the cleanest approach.

    0
    • 83

      I have the same issue, although the code seems to function as expected. Am I doing something wrong by using createRecord like this? I’m a little confused by the error message.

      0
      • 84

        I ran into this issue as well. and via this link https://github.com/emberjs/data/blob/master/TRANSITION.md

        changed my


        // usersCreateRoute.js
        App.UsersCreateRoute = Ember.Route.extend({
        model: function(){
        return this.store.createRecord('user');
        },
        // reuse the edit template
        renderTemplate: function(){
        this.render('user.edit', {
        controller: 'usersCreate'
        });
        }
        });

        and my
        // usersCreateController.js
        App.UsersCreateController = Ember.ObjectController.extend({
        actions: {
        save: function(){
        var newUser = this.get('model');
        newUser.set('creationDate', new Date());
        newUser.save();
        this.transitionToRoute('user', newUser);
        }
        }
        });

        0
  38. 85

    Dmitry Pashkevich

    February 3, 2014 6:40 am

    Why are you using `Em.Object.create({})` to create a new User model instance instead of `App.User.create({}) ` ?

    0
  39. 86

    Dmitry Pashkevich

    February 4, 2014 4:42 am

    Hey I noticed a fun detail in your doctype:

    Is that a hint to the browser for unicode or just a cosmetic thing?

    0
    • 87

      Pure cosmetic, I call it *the luv doctype* :P
      And yes you could use App.User.create instead of Em.Object.create. At the end, UsersCreateController converts it into an App.User object.

      0
  40. 88

    Hi, thanks for the great overview. I am having a issue when using the back-button in my browser. What happens is, the nested elements do not disappear (for instance: the edit-form stays on screen, after hitting the ok-Button ). They disappear only after refreshing the page. Could anybody help me out. =)

    0
  41. 89

    Hey, I think this must be one of the most awesome guides for ember on the internet, it would be nice to have a updated version of this guide.
    For instance, using Em.Object.create({}); apparently doesn’t work anymore (at least is not working for me, on Ember 1.3.1 =P)

    2
  42. 90

    I have compiled your article into a Google Doc which can be viewed and be downloaded easily. It has been an essential document in me learning the framework!

    Thank you for the effort and time! Much love from India!

    https://docs.google.com/document/d/1Qf6RkPB0GMn56IXpKbG9XO7RcWyVDglC8o6TsLZWqEc/edit?usp=sharing

    0
  43. 91

    I’m completely new to Ember and trying to follow along but I feel like a few steps have been skipped here. The index.html doesn’t actually pull in the app.js file, are we supposed to include that manually? If I do see the app appear in the Ember debugger in Chrome, but no routes. So I manually include router.js too, and that helps. But with so many other files in so many other subdirectories I get the feeling I shouldn’t be doing this manually.

    0
    • 92

      Indeed you should have a build tool to concatenate all you small js files. Otherwise you’d have to write everything in one single js file and then you’d load it into your page. It’s up to you, personally I use Grunt as my build tool.

      0
  44. 93

    Do you have any plan to create EAK version of this app?
    I tried the users app above from Andy Baudoin, but it didn’t work.
    Thanks for great sharing.

    0
  45. 95

    I know this tutorial is a few months old but it really helped me wrap my head around some of the Ember concepts. Our startup RadiumCRM (http:/ /www.radiumcrm.com) is built on EmberJs and we really took advantage of all the framework has to offer. Its quite an ambitious project but its a good reference to anyone starting out. The benefits of a single-page app are really awesome!

    0
  46. 96

    Thanks for this guide! I just started working with Ember and this guide was a really good introduction!
    I definitely recommend this guide to those who are starting with Ember.

    0
  47. 97

    It is an absolutely great tutorial to start learning ember, however, i would like to ask you a question about adding a new user. When i press the button ‘Add User’, a new layout for ‘Edit User’ shows up. If i enter no user information it also adds to the user list section as a blank user. What i believe is it should check for input values. At least for the user name.

    I hope my statement is clear to understand.

    Can’t wait for the next tutorial about learning ember.

    0
    • 98

      True indeed, I haven’t covered the fields validation subject.

      On your UsersCreateController you could use a computed macro — which is a simpler/cleaner/faster way to write a computed property — to test if the name input isn’t empty.

      /* UsersCreateController */

      isNameNoEmpty: Ember.computed.notEmpty('name'), // boolean

      save: function(){

      if ( this.get('isNameNoEmpty') ) {
      createRecordAndSave...
      }

      Should do the trick nicely :)

      Also check out Ember Validation which is a little library you can plug to handle all kinds of fields validations.

      1
  48. 99

    Great article! I had more than one ‘aha’ moments reading it :)

    0
  49. 100

    Douglas Russell

    April 11, 2014 1:32 pm

    Hi,

    Firstly, really great tutorial. Couple of things though.

    You make reference to automatically created Controllers/Routes a few times, such as:

    ‘Ember will guess that we are dealing with a collection because we’ve used the plural of “user.”’

    and

    ‘Hey, wait a minute! We have declared neither UserRoute nor UserController, but it’s still working! Why is that? Well, UserRoute is the singular of UsersRoute, so Ember has generated the route and the controller for us (in memory). Thank goodness for naming conventions!’

    Maybe I’m misinterpreting your intended point here, but I don’t think that’s true. In the first case I think Ember knows you’re dealing with a collection because its model returns a list, not because of the plurality of the name:

    return this.store.find(‘user’);

    In the second case, I don’t think the automatically generated route and controller have anything to do with the fact that they are the singular of an existing plural route, but rather that anything defined in the route map has these auto-generated artifacts as you do actually say near the top when you say: ‘This Router will generate exactly this:’, and then display the table of routes/components from your browsers Ember inspection module.

    0
  50. 101

    Samuel deHuszar Allen

    April 16, 2014 2:50 am

    I’m not sure if it’s an issue with newer versions, but the Em.Object.create({}) portion of the tutorial did not work for me. I had to use this strategy to get my entries to POST properly:

    http://stackoverflow.com/questions/21344539/emberjs-cannot-clone-an-ember-object-that-does-not-implement-ember-copyable#answer-22382621

    Really great tutorial otherwise. I’ve been super productive with this as a reference point.

    Another thing to detail, for anyone else trying to port this over to Ember App Kit, if you end up reusing the edit template for your create controller and you want to use subfolders for organization, you’ll want to define it like so:

    renderTemplate: function(){
    this.render(‘workhistory.edit’, {
    controller: ‘workhistories.create’
    });
    }

    Hopefully that saves someone else some time. :)

    -1
  51. 102

    Good amount of work/effort put into it. Haven’t been bale to read the entire entry but hats off for putting in this much effort.
    Truly appreciate it.. :)

    0
  52. 103

    Wow, one of the best introduction/guide on Ember.js I have ever read, too good for all level of JavaScript developers.

    0

Leave a Comment

Yay! You've decided to leave a comment. That's fantastic! Please keep in mind that comments are moderated and rel="nofollow" is in use. So, please do not use a spammy keyword or a domain as your name, or else it will be deleted. Let's have a personal and meaningful conversation instead. Thanks for dropping by!

↑ Back to top