According to the creators of the framework, Ember.js is “a framework for creating ambitious web applications”. What exactly do they mean? Also, with the many (, many, many) JavaScript frameworks that we have to choose from these days, why might I choose Ember? In this post, I’ll introduce Ember.js and some of the core concepts that backed its development. In future posts, I’ll dig in and show you how to build applications with the framework. For now, let’s look at what the framework is made of.
Guiding principles
Before looking at the details of any framework, it helps to understand the core concepts that drove the feature decisions in the first place. Perhaps the biggest factor in the creation of Ember was the belief that what makes the web so unique is the ability to bookmark and share URLs. The team felt that many JavaScript frameworks treated the URL as an afterthought (mostly by treating the concept of a router as an optional add-on). The Ember team also wanted to create an MVC framework that was closer in line with the desktop MVC framework mentality rather than taking a server-side MVC approach (like ASP.NET MVC or Ruby on Rails). So, Ember combines the productivity of a native UI framework while supporting shareable URLs. The following key concepts are what make up the framework from a high-level overview standpoint.
Core concepts
Naming conventions
Ember was written with convention over configuration in mind. This means that much of the framework relies on things being named consistently and in the manner that the framework expects. The naming conventions are summarized nicely here.
Templates
Templates are what describe the user interface in Ember.js. Ember templates are written using the Handlebars templating language which allows for expressions to be added to standard HTML markup within double curly braces: {{}}. Each template is backed by a model and supports databinding such that the template will auto-update when the model changes. The following features are supported in Ember’s Handlebars templates:
- Plain HTML
- Expressions - Allow model values to be substituted into the HTML. For instance, {{price}} would substitue the value of the price property on the model into the template where specified
- Outlets - An outlet (specified by {{outlet}}) is a placeholder for another template. As the user moves around the application, different templates can be loaded into the outlet by the router.
- Components - Custom HTML elements (technically, custom Handlebars helpers) that help clean up repetitive templates by enabling the creation of reusable controls
Example template:
Name: {{input type="text" value=name placeholder="Enter your name"}}My name is {{name}} and I want to learn Ember!
Router
The router in Ember is what powers the shareable URLs that are so important to web applications. The router translates the URL of a request into a series of templates. For instance, when I visit /coffee within the application, Ember’s router will translate this to the CoffeeRoute which will set up the appropriate template backed by the appropriate model for that request. As the templates or models being shown to the user change, Ember’s router will automatically keep the URL in the browser up to date. This means that the user can share the URL at any point in the application lifecycle. Whenever someone clicks on this shared URL, they will see the same thing that the original user saw.
Example router:
App.Router.map(function() { this.resource('about'); this.resource('coffees', function() { this.resource('coffee', {path: ':coffee_id'}); }); }); App.CoffeesRoute = Ember.Route.extend({ model: function () { return App.Coffee.find(); } });
Models
A model object is used to store persistent state within an Ember application. These model objects are what back the templates and provide data to be displayed within the HTML. Many applications will load these models through a REST API using Ember Data which will store the data in a server database, but Ember doesn’t limit you to just this choice. There are many adapters for Ember Data that allow you to choose the backend that works best for your project. You can even roll your own syncing using the Basic Adapter.
Example model:
var attr = DS.attr; App.Coffee = DS.Model.extend({ name: attr('string'), price: attr('number'), ounces: attr('number'), description: attr('string'), roaster: attr('string'), acidity: attr('string'), body: attr('number') });
Controllers
The controller is technically where the templates get their data from. Though optional, a controller will be created by Ember if not directly declared for a route. The controller “decorates” the model object. Properties specified on the controller can be accessed by a template as if they were declared on the model. Methods can be created within the controller that can be called using Handlebars helpers in the template. This enables things like button actions. In general, properties that do not need to be synced to the backend server but are necessary for template logic should be created in the controller. Ember supports the creation of ObjectControllers, which manages a single model object, and ArrayControllers which manage a list of model objects.
Example controller:
Todos.TodoController = Ember.ObjectController.extend({ isCompleted: function(key, value){ var model = this.get('model'); if (value === undefined) { // property being used as a getter return model.get('isCompleted'); } else { // property being used as a setter model.set('isCompleted', value); model.save(); return value; } }.property('model.isCompleted') });
Components
Components allow for the creation of reusable controls. The interface for the component is defined using Handlebars templates. The component also has a JavaScript class that is used to store state and handle user interaction. Components are placed into a template using a Handlebars helper with a name based off of the name of the component.
Example component:
{{input type="email" value=email placeholder="Enter your Gravatar e-mail"}}
App.GravatarImageComponent = Ember.Component.extend({ size: 200, email: '', gravatarUrl: function() { var email = this.get('email'), size = this.get('size'); return 'http://www.gravatar.com/avatar/' + hex_md5(email) + '?s=' + size; }.property('email', 'size') });
Summary
Ember.js is a relatively young but growing framework. Though in the past it suffered from a lack of documentation and a difficult learning curve, that situation has improved dramatically. The Ember guides are very well-written and will help you get started with the framework in no time. I will also be posting more on how to develop with this framework over the coming weeks.
Contact
If you want to comment or reach out to me, the best place to do that is on Twitter @brentschooley. I can also be reached via email at bschooley@infragistics.com.