Building Ambitious Web Applications with Ember.js

So, a couple weeks back I gave my first ever technical talk at CambridgeJS called 'Building Ambitious Web Applications with Ember.js'. The talk gave a short introduction to the Ember.js framework and I thought it could be a good idea to write a corresponding blog post to go with it.

In this blog post, I'll talk about the core concepts of the Ember framework and give a demo of how to build an Ember application using a simple book management app as an example. This post assumes a basic knowledge of JavaScript and web development, but no previous experience of Ember is required.

ember-slides-intro

The slides from the talk are available here and the source code for the demo application is available on GitHub.

What is Ember?

Ember is a framework for building ambitious web applications. By ambitious, we mean that web applications built with Ember can utilize features that are usually only available on native apps. This means Ember apps can have 'states' where the application logic is able to be managed by what state the app is currently in. For example, if the user is authenticated we can display the interface differently than if the user is unauthenticated. Ember apps are also typically long-lived, meaning that the app can be open for hours at a time without having to be refreshed, and the user is still able to make new data requests.

Ember is built upon the principle of 'Convention over Configuration'. This means that the framework is very heavily opinionated and there are a lot of conventions regarding the best practices of developing apps in Ember. This can be a good or bad thing depending on your preference and knowledge levels. For example, if you are taking over a large code base, and are unfamiliar with the conventions of Ember, you could find yourself debugging everything to get to the root of the problem. But on the positive side, this dramatically reduces the amount of decisions you have to make and can allow you to get straight into building the functionality of your app.

Ember uses the Handlebars templating language to craft the markup of your applications. This means you can write significantly less code in your templates and use curly brace helpers to provide more functionality to your HTML. You can also use Ember Data to pass real-time data to your templates using Handlebars helpers.

The Ember Stack

Ember is built up on a stack of different technologies that help to make it as functional as possible. This takes care of a lot of the configurations for things such as build tools and saves us time in setting those up.

Ember.js - the core MVC/MVVM framework.

Ember CLI is the official command line utility for building Ember apps. It allows us to build new projects with a strong project structure, as well as generate Ember classes and install add-ons.

Ember Data - is a data persistence library that works with Ember models.

The Add-on community is a large collection of external libraries that help to solve problems that Ember doesn't by default. They are listed at emberobserver.com, which has a nice rating system to find the best add-ons based on how well documented they are and how well they are maintained.

Ember Inspector is a great browser extension for debugging Ember apps in Chrome or Firefox. You can also use the Inspector to 'inspect' Ember Data models to check if the data is being returned from the server as expected.

QUnit is the default testing framework for Ember, automatically generated and configured when building a project with Ember CLI.

Ember CLI

Ember CLI is the official command line utility that provides the user with fast build tools (powered by Broccoli), a default project structure, add-on installations and generators for Ember objects.

You can install it through the terminal, but it requires Node.js, npm and Bower to be pre-installed globally. Once it has been installed you can check the currently installed version by running ember -v.

$ npm install -g ember-cli
$ ember -v
  ember-cli: 2.6.2
  node: 5.2.0
  os: darwin x64

Once you have confirmed that it has been installed, we can use it to generate a brand new Ember project.

$ ember new <app-name>
$ cd <app-name>
$ ember server

When you are ready to run the application in the development environment, all you need to do is run the ‘ember server’ command on the root of the directory and it will automatically run a build and host your application on localhost:4200.

Project Structure

When you have created a new project with Ember CLI, the app structure is laid out in the default way. If you cd into your app and take a look, it will look something like this:

|--app/ # Core Ember functionality
|--bower_components/ # bower dependencies
|--config/ # Configurations
|--dist/ # Build output
|--node_modules/ # npm dependencies
|--public/ 
|--tests/ # Test framework
|--tmp/ # temp files
|--vendor

bower.json  
ember-cli-build.js  
package.json  
README.md  
testem.js  

The app/ directory is where the application code is written. In here you will have all of your routes, models, templates and components that create the core parts of the app. All of the JS files in this directory are automatically compiled through the ES6 module transpiler Babel and concentrated into a single file.

Core Concepts

The core architecture of an Ember application is inside the ‘/app’ directory. In here you’ll find folders for the routes, models, templates, controllers, components and all other Ember concepts.

core-concepts-diagram

The basic flow of an Ember application is shown in the diagram. Every state in the app has a specific URL, which is defined in a number of ways. For example, the user clicks links within the app to navigate between pages. The Router maps the current URL to a Route (or route handler). The Route is able to render a Template view for the state and load data into it from the Model. The template can then display that data using Handlebars expressions. Ember Components can also be injected into the templates to create a more reusable UI.

Controllers can act as the logic between a route and its template. They are soon to be fully replaced with routable components, which is under development and aimed to be released in version 3.x so they will not be included in this post.

Now that we have explored the core concepts of the framework a little, let's jump into writing a demo application for managing books. To get started, we'll generate a new Ember project using Ember CLI called Flint Books.

$ ember new flint-books

Router/Routes

So the first concept of an Ember app is the URL mappings in the router. The router allows a user to navigate to a specific URL, /books for example, to render the specified route. The route is then able to render a template for the URL.

We can now generate some routes using Ember CLI. We will generate an application route, an about page and a route for listing our books.

$ ember g route books
installing route  
  create app/routes/books.js
  create app/templates/books.hbs
updating router  
  add route books
installing route-test  
  create tests/unit/routes/books-test.js

When you generate a route with Ember CLI it will automatically create a route .js file, a .hbs template and a test for that route. It will also automatically map the new route into the router so if you were to navigate to that particular route in the URL, it would render an empty view as expected.

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

const Router = Ember.Router.extend({  
  location: config.locationType
});

Router.map(function() {  
  this.route('about');
  this.route('books');
});

export default Router;  

So now if we check out the code inside of our Router, we can see that the about and books routes have been mapped.

You may notice that the application route has not been mapped into the router, this is because this is the default route and will be rendered when the user first loads up the app.

Templates

Templates organize the layout of the applications HTML. All HTML is valid in the template, with the addition of Handlebars syntax. Templates can also display properties passed into them from their current content, which is the route or the component. This is done with curly brace syntax, for example {{name}}.

Our application template contains HTML that will be displayed across all routes of our application. This means that this is a good place to put out navbar and footer components. We can then render the content for other routes inside of the {{outlet}} helper.

<!-- app/templates/applications.hbs -->

{{#link-to 'application'}}Home{{/link-to}}
{{#link-to 'books'}}Books{{/link-to}}

<div class="container">  
 {{outlet}}
</div>  

This template for the application route shows that we have {{link-to}} helpers allowing the user to navigate to the other routes. The {{link-to}} helpers will render the route we've specified without reloading the page, keeping with the single-page application logic. It will also add a .active class to the active link, allowing us to add styling to the links depending on their state.

<!-- app/templates/books.hbs -->

<h2 class="center-align">This is the Books route</h2>  

With the books template looking like this, if the user were to click the books link, they would be displayed with the 'This is the Books route' header. The links would also be rendered on the page, as they were specified outside of the application route's {{outlet}}.

Now that we have our routes and templates ready, let's add some content to the application.

Add-ons

Add-ons are reusable libraries that can solve problems that Ember doesn’t by default. They can be installed with Ember CLI and are added to your list of dependencies in your package.json file. For this example we are going to install two Ember add-ons, ember-cli-mirage and ember-cli-materialize.

Mirage acts as a mock server and allows you to quickly test and build your application by using the mock API. Materialize is a CSS framework for building Google's material design into your application. Using the ember-cli-materialize add-on we are able to quickly implement material styling in our app without having to write any CSS.

$ ember install ember-cli-mirage
$ ember install ember-cli-materialize

Once Mirage is installed using Ember CLI a new folder will be generated at the root of the directory called mirage/. Inside here we can configure some mock data for some books to display in the application. For brevity, I’ve only displayed code for one record being inserted into the configuration file, but we can add as many as we like and in this instance I've inserted five book records.

// mirage/config.js
export default function() {  
  this.get('/books', function() {
    return {
      data: [{
        type: 'books',
        id: 1,
        attributes: {
          title: 'Norwegian Wood',
          author: 'Haruki Murakami',
          year: '1987',
          image: 'https://d.gr-assets.com/books/1386924361l/11297.jpg',
          blurb: 'This stunning and elegiac novel by the author of the internationally acclaimed Wind-Up Bird Chronicle has sold over 4 million copies in Japan and is now available to American audiences for the first time.It is sure to be a literary event.'
        }
      }]
    };
  });
}

So now we have some mock data ready to be inserted into our application. To be able to use our Mirage data, we will need to configure a book data model using Ember Data.

Ember Data

The Ember Data library is installed into all Ember projects by default. Ember Data is a data persistence library which allows us to create, update and delete model records from the store.

Ember Data allows you to easily retrieve data from the server in JSON format. It also uses the adapter pattern to work with many different types of backend architecture, if it does not return JSON by default. For example, there is an ember-django-adapter that would allow us to configure our Ember frontend to align with a Django backend and return data in the appropriate format.

We can now generate a book model to persist the book data we created with Mirage.

$ ember g model book
installing model  
  create app/models/book.js
installing model-test  
  create tests/unit/models/book-test.js

Models are objects that represent the data that the application displays to the user. They are persistent which means the user will not lose any model data if they close the browser. The model data needs to be stored somewhere in order to persist the data when it is changed. Once the models have been loaded the model data can be translated into the templates to create an interactive UI.

We can now define the type of data we are expecting to be persisted in the book model.

// app/models/book.js
import Model from 'ember-data/model';  
import attr from 'ember-data/attr';  
// import { hasMany, belongsTo } from 'ember-data/relationships';

export default Model.extend({  
  title: attr('string'),
  author: attr('string'),
  year: attr('number'),
  image: attr('string'),
  blurb: attr('string')
});

You can see here that we are expecting five fields, a title, author, year of publication, a link to a cover image and a blurb.

Now, if we go back to our books route we can call a model hook to find all of the book records we have stored so they are ready to be injected into our books template.

// app/routes/books.js

import Ember from 'ember';

export default Ember.route.extend({  
  model() {
    return this.store.findAll('book');
  }
});

Now, we can display the data in our template. We can use the {{each}} Handlebars conditional to loop through all book records and display them as specified in the markup.

<!-- app/templates/books.hbs -->

{{#each model as |book|}}
  <h3>{{book.title}}</h3>
  <h4>{{book.author}} - {{book.year}}</h4>
  <p>{{book.blurb}}</p>
{{/each}}

So if we were to then head to [localhost:4200/books](http://localhost:4200/books) we would see the books data as expected.

books-template

Ember Inspector

The Ember Inspector is a browser extension that can be installed for Chrome and Firefox. It adds an extra tab to your developer tools to allow you to inspect and debug Ember objects in your app.

ember-inspector-results

In the screenshot, we can see that the Ember Inspector tab is open and we can see a display of all Ember Data models in our app. As expected, you can see the book model has loaded with 5 records, each displaying the data for each field.

Our application is working as expected and renders all of the content we have asked it to. Now we have it in a stable condition, we can create components to make the UI more reusable.

Components

Components are reusable UI elements that define how a specific chunk of the interface looks and behaves. They consist of two files: a Handlebars template to structure the markup and a JavaScript file that defines the behaviour of the component. Components must be namespaced with a dash so they can compile with the new W3C Custom Elements spec.

Components can be generated using Ember CLI in the same way as before. The .hbs and .js file are both automatically created, again with an integration test in the tests directory.

In the books app example we could create components for the applications navbar, footer and even a book-listing component to replace the markup in the books template.

$ ember g component book-listing
installing component  
  create app/components/book-listing.js
  create app/templates/components/book-listing.hbs
installing component-test  
  create tests/integration/components/book-listing-test.js

We could build up a UI for each book listing in the component using some of the Materialize helpers from the library.

<!-- app/templates/components/book-listing.hbs -->  
<div class="col s6 m4 l3">  
  {{#md-card class="medium" titleClass="indigo-text" title=book.title image=book.image activator=true id="card-reveal"}}
    {{#md-card-content}}
      <h6>{{book.author}} ({{book.year}})</h6>
    {{/md-card-content}}
    {{#md-card-reveal}}
      {{book.blurb}}
    {{/md-card-reveal}}
  {{/md-card}}
</div>  

In the books template, we can then simply call the book-listing component with the curly braces. We can then pass in the book model to the component so that the data is able to bubble down and be rendered by the component.

<!-- app/templates/books.hbs -->  
<h2 class="center-align">Books</h2>

<div class="row">  
  {{#each model as |book|}}
    {{book-listing book=book}}
  {{/each}}
</div>  

Now if we navigate to localhost:4200/books in the browser, we can see the display of books with a nicer UI that is reusable and will automatically update when new book records are created in the application.

books-app-with-components

Summary

So hopefully now we should have a general understanding of the core concepts of the Ember.js framework and the knowledge to create a simple, but functional, Ember application. If you want to check out the source code for the books app demo, you can find it on GitHub.

Below are some of my favourite resources for learning Ember. The official documentation is very well maintained, so I definitely recommend checking out the guides first.

Originally published at danielgynn.com.

Read more posts by Daniel Gynn

Front-end developer.