Blog of Appliness

Flame on! A beginner’s guide to Ember.js

flame

This tutorial appeared in the June issue of Appliness. You can click here to see the original post on ADC by Andy Matthews.

Sophisticated JavaScript applications can be found all over the place these days. As these applications become more and more complex, it’s no longer acceptable to have a long chain of jQuery callback statements, or even distinct functions called at various points through your application. This has led to JavaScript developers learning what traditional software programmers have known for decades: organization and efficiency are important and can make the difference between an application that performs great and one that doesn’t.

One of the most commonly used architecture patterns to achieve this organization and efficiency is known as Model View Controller (or MVC). This pattern encourages developers to separate distinct parts of their application into pieces that are more manageable. Rather than having a function that makes a call directly to the database, you create a Model to manage that for you. Instead of having an HTML file sprinkled with output and logic statements, a simple template, or View, allows you to streamline your display code. Finally a Controller manages the flow of your application, helping the various bits and pieces talk to each other more efficiently. Using this pattern in your application makes it easier to add new functionality.

As part of the recent explosion of Internet-based software development, a dizzying array of MVC frameworks with names like Ember.js, Backbone.js, Knockout.js, Spine.js, Batman.js, and Angular.js have emerged. Written in JavaScript and designed for JavaScript development, these libraries have filled the void between beginner and intermediate developers on one side, and hardcore programmers on the other. They offer various features and functionality that will suit different developers of varying skill levels based on their needs.

In this tutorial you’ll become more familiar with the basics of Ember.js as you build a working Twitter timeline viewer.

Introducing Ember.js

Ember.js (under that name) is one of the newest members of the JavaScript framework pack. It evolved out of a project called SproutCore, created originally in 2007 and used heavily by Apple for various web applications including MobileMe. At emberjs.com, Ember is described as “a JavaScript framework for creating ambitious web applications that eliminates boilerplate and provide a standard application architecture.” It comes tightly integrated with a templating engine known as Handlebars, which gives Ember one of its most powerful features: two-way data-binding. Ember also offers other features such as state management (is a user logged out or logged in), auto-updating templates (when the underlying data changes so does your UI), and computed properties (firstName + lastName = fullName). Ember is already a powerful player after a solid year’s worth of development.

Ember has only one dependency—jQuery. The boilerplate HTML setup for an Ember application should look something like the code below. Note that both jQuery and Ember are being pulled from a CDN (content delivery network). This speeds up your users’ page load if they have already downloaded these files as a result of earlier visits to other websites that require them.

<html> 
<head> 
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> 
    <script src="http://cloud.github.com/downloads/emberjs/ember.js/ember-0.9.6.min.js"></script> 
    <script src="js/app.js"></script> 
</head> 
<body> </body> 
</html>

Defining MVC

Before you proceed with this tutorial it would probably be a good idea to more clearly define MVC. The concept has been around since 1979 and since that time a number of different variations on the pattern have emerged. The most common flow usually goes something like this:

  1. A user performs an action such as typing on the keyboard or clicking a mouse button.
  2. The Controller receives that input and fires off a message to the Model.
  3. The Model changes its content based on the message (deletes a row or updates shopping cart quantities).
  4. The View watches for a change in the Model and updates the user interface accordingly.

Understanding how the MVC pattern works can make your application flow more easily. And, because code is split into distinct pieces, it’s easier for teams of developers to work together without interfering with each other.

How Ember does MVC

JavaScript is a flexible and powerful language but it also has its shortcomings. Out of the box it doesn’t offer the sort of functionality that lends itself to MVC style development. So Ember has extended the base language with a slew of extras. When building your Ember application there are four main pieces that you’ll be working with: Application, Model, View, and Controller. The following sections review each of these pieces.

Application

Every Ember application requires an instance of Ember.Application. It’s the basis for the entire rest of your code, and provides useful functionality as well as a namespace (a way of grouping the rest of the pieces of your app). Defining an Ember application is simple:

Songs = Ember.Application.create({ 
    mixmaster: 'Andy' 
});

This code defines an application named Songs with a property named mixmaster set to Andy . You can call your application whatever you like, but Ember requires the variable name to begin with a capital letter so that the binding system can find it. There are additional built-in options that can be added when creating your application, and you can add any arbitrary property or method as well, but the main one beginning users might care about is the ready() method. This works exactly like jQuery’s document.ready() block and can be implemented in the following manner:

Songs = Ember.Application.create({ 
    mixmaster: 'Andy', 
    totalReviews: 0, 
    ready: function(){ 
       alert('Ember sings helloooooooooo!'); 
    } 
});

Models

An application is nothing without data. Ember helps developers manage this data in a structured way using Models. In addition to holding data, Ember Models also model the data within them. In other words, if you wanted to store information about your MP3 collection, your Model might contain a title property, an artist property, a genre property, and so on. That Model might look something like this:

Songs.Song = Ember.Object.extend({ 
   title: null, 
   artist: null, 
   genre: null, 
   listens: 0 
});

There’s a few things to note about these few lines.

  • Right away you can see your application’s namespace in use. Songs is the name of the application, while Songis the name of the Model.
  • When extending an object you’re creating a blueprint for future instances of this Model. Because this is themaster object all songs will be based on, it uses a capital letter. These naming conventions will make it easier down the road to tell what sort of object you’re working with.
  • When creating your Model you can provide default values for each property. The title , artist , and genreproperties will obviously be filled in later, and so are marked null (or nothing). The listens property defaults to 0 and its value will increase as you listen to your music collection.

Now that the Song model is in place, you can add your first song. You used extend to initialize the Song model, but you’ll use create to add an instance of it. Here’s what that looks like:

mySong = Song.create({ 
   title: 'Son of the Morning', 
   artist: 'Oh, Sleeper', 
   genre: 'Screamo' 
});

Notice that the variable doesn’t begin with an upper case letter, that’s because it’s an instance of the Song model. The new song also isn’t within the Songs namespace. You’ll almost never create an instance of a Model within your application. You’re certainly welcome to do so, but generally you’d place each instance of a Model within a larger collection of similar objects such as an ArrayController (more on that later).

Views

In an Ember application or any MVC style application a View is something the user can see and interact with. You define an inline template by adding raw HTML directly to the page. This template will be contained within scripttags. You add it to the page wherever you want your content to appear.

<script type="text/x-handlebars"> 
   Hello <b>{{Songs.mixmaster}}</b> 
</script>

Notice that the script tag has a type of text/x-handlebars . This gives Ember something to grab on to when it loads up the page. Any HTML contained within this script tag is automatically prepared by Ember for use in your application. Placing these lines of code within your application will display the following text:

Hello <b>Andy</b>

Before moving on, take a peek under the hood. In your browser, right-click the bold text and inspect it using the browser’s dev tools. You might notice some extra elements. In order to know which part of your HTML to update when an underlying property changes, Handlebars will insert marker elements with a unique ID; for example:

<b> 
   <script id="metamorph-0-start" type="text/x-placeholder"></script> Andy 
   <script id="metamorph-0-end" type="text/x-placeholder"></script> 
</b>

You can also define a View directly in JavaScript, and then display it to the page by using a view helper. Ember has generic views that create simple div tags in your application, but it also comes prepackaged with a set of views for building basic controls such as text inputs, check boxes, and select lists. You start by defining a simple TextAreaView within your JavaScript file.

Songs.ReviewTextArea = Ember.TextArea.extend({ 
   placeholder: 'Enter your review' 
});

Then display it to the page by referencing the path to the variable containing the view, prefaced by the word view . Running the following code in your browser displays a TextArea field with placeholder text of “Enter your review”. You can also specify rows and cols as additional properties in your definition.

<script type="text/x-handlebars"> 
   {{view Songs.ReviewTextArea}} 
</script>

Handlebars

By now you’re probably wondering what the {{ and }} in the code stand for, so this is a perfect time to talk about Handlebars, also known as mustaches. Turn your head sideways and you’ll see why they’re called Handlebars pard’ner. Handlebars is a templating engine that lets developers mix vanilla HTML and Handlebars expressionsresulting in rendered HTML. An expression begins with {{ and ends with }} . As discussed previously, all templates must be placed within script tags with a type of text/x-handlebars .

By default, any value contained within handlebars is said to be bound to its value. That means that if the value changes because of some other action within the application, the value displayed to the user will update as well. Consider the following code:

<script type="text/x-handlebars"> 
   My songs have {{Songs.totalReviews}} reviews. 
</script>

When your application first initializes the user would see the following text.

My songs have 0 reviews.

But through the magic of data bindings, that value would change in real time as additional reviews were added by updating Songs.totalReviews .

Handlebars also supports flow control through the use of {{#if}} and {{else}} . These elements let youconditionalize your templates based on values in your application. You could change the previous example to display an alternate message to the user when there are no reviews:

<script type="text/x-handlebars"> 
   {{#if Songs.totalReviews}} 
      Read all my reviews! 
   {{else}} 
      There are no reviews right now. 
   {{/if}} 
</script>

If at any point in the life of the application, the Songs.totalReviews value changes, the view will update and display the other part of the message. It’s also worth noting that the # and / symbols are merely there to tell Handlebars that this particular view helper has a closing part.

Controllers

Earlier, the Model was defined as a way to enable developers to manage data. That’s true, but only in a very narrow way. A Model only contains data about a single thing; for example, a song (but not songs) or a person (but not people). When you want to manage multiple pieces of the same type of data you need a Controller. With Ember you can use an ArrayController to manage sets of songs, people, widgets, or whatever. Each ArrayController has a built-in content property that is used to store data. This data can be simple strings or complex values such as arrays or objects. Additionally, ArrayControllers can contain functions that are used to interact with the data contained within them. What might an ArrayController for your Song collection might look like?

Songs.songsController = Ember.ArrayController.create({ 
   content: [], 
   init: function(){
               // create an instance of the Song model
               var song = Songs.Song.create({
                     title: 'Son of the Morning',
                     artist: 'Oh, Sleeper',
                     genre: 'Screamo'
               });
               this.pushObject(song);
         }
});

The init function isn’t required, but comes in handy as it will be triggered as soon as songsController is ready. It could be used to populate the controller with existing data, and in this case you’ll use it to add a single song to the Controller to illustrate Ember’s data-binding. Add the previous ArrayController definition and the following inline template and run the code in your browser:

<script type="text/x-handlebars"> 
   {{#each Songs.songsController}} 
      <h3>{{title}}</h3> 
      <p>{{artist}} - {{genre}}</p> 
   {{/each}} 
</script>

The Handlebars each helper receives a path to a set of data, and then loops over it. Everything inside the matchingeach blocks will be displayed on the page for every item in the controller. Notice that you’re not providing a path directly to the content array, because as far as Ember is concerned the controller is the array. The resulting HTML output looks like this:

<h3>Son of the Morning</h3> 
<p>Oh, Sleeper - Screamo</p>

Putting it all together: EmberTweets

At this point, you should have a good understanding of what Ember is and what it can do. You should also understand each of the pieces that enable Ember to work its magic: Application, Model, View, and Controller. It’s time to put that knowledge to use in writing a real, working application. You’re going to skip the industry standard “todo app” and move on to something near and dear to many: Twitter. In the rest of this tutorial you will be building a Twitter timeline viewer. Before writing any code, it might be useful to see the final result.

Creating your boilerplate files

Using the boilerplate HTML page from the beginning of the article you’ll first build out the base HTML. Copy and paste the following code into a new HTML file named index.html. You’ll need to reference the CSS file found in the sample files for this article. The sample files also contain a starting point for this project so feel free to use that as well.

<!doctype html>
<html>
<head>
   <title>Tweets</title>
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <link rel="stylesheet" href="styles.css">
   <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
   <script src="http://cloud.github.com/downloads/emberjs/ember.js/ember-0.9.6.min.js"></script>
   <script src="app.js"></script>
</head>
<body>
   <script type="text/x-handlebars">
      <div id="frm">
         <b>Load Tweets for: </b>
      </div>
      <div id="content">
         <div id="recent">
            <h3>Recent Users</h3>
         </div>
         <div id="tweets">
            <h3>Tweets</h3>
         </div>
      </div>
   </script>
</body>
</html>

You can see there are three parts to this application: an input field, which allows users to input a Twitter username, the timeline viewer, which displays the selected Twitter users’ tweets, and a recent users list, which will store previous searches.

The search box will appear at the top of the page, the recent users in a column to the left, and the tweets themselves will have the majority of the page on the right side.

Next, create another file named app.js and add the following content. These comments helps you keep your code organized. Load this page up in your browser and make sure there are no errors.

/************************** * Application **************************/ /************************** * Models **************************/ /************************** * Views **************************/ /************************** * Controllers **************************/

Application initialization

The first thing you’ll need to do is to initialize your application. Directly under the comment block labeled Application, place the following code:

App = Em.Application.create();

Notice that instead of saying Ember.Application, this line says Em.Application. The Ember team added this handy shortcut to reduce typing by allowing you to use “Em” in any place where you might use “Ember”.

Next you’ll add the TextInput view and the submit button. Directly under the comment block labeled “Views” add the following code:

App.SearchTextField = Em.TextField.extend({ 
   insertNewline: function(){ 
      App.tweetsController.loadTweets(); 
   } 
});

This block starts by using the App namespace, then extends one of Ember’s prepackaged Views, the TextField. In addition to allowing arbitrary properties and functions within Views, Ember also has built-in helper functions available for use. That’s what the insertNewLine() function is; it executes whenever the user presses the Enter/Return key on their keyboard while the cursor is within the input box.

Creating template blocks

Now that the TextField View is defined, you’ll add the corresponding view helper code to the HTML file. Switch to index.html and add the following code directly after the line that reads “Load Tweets for”. Remember that anything within {{ and }} is a template and will be used by Ember to output data. Additionally any template beginning with the word view refers to a View that has been defined within your JavaScript code.

{{view App.SearchTextField placeholder="Twitter username" valueBinding="App.tweetsController.username"}} 
<button {{action "loadTweets" target="App.tweetsController"}}>Go!</button>

This portion of the template contains a view helper, and a button tag with an {{action}} helper. The TextField View, SearchTextField , begins with an attribute that is built into HTML5 text input fields, placeholder text. If the field is empty, the text within the placeholder attribute will be placed into the input field. When someone begins typing, the value goes away. Ember enables developers to use any HTML 5 standard attributes within its built-in views.

The second attribute highlights the magic of Ember’s data-bindings. Ember uses a set of conventions to help it determine what you’re trying to accomplish. Any attribute in a view (either within a template, or in a JavaScript file) that ends with the word “Binding” (note the capital letter) automatically sets up a binding for the attribute that precedes it. In this case Ember is binding the value of App.tweetsController.username to the input field’svalue attribute. Anytime the contents of the variable changes, the value contained within the input field will update automatically, and vice versa.

The {{action}} makes it easier to add functionality to input driven elements. It has two options: the action name and the target. Taken together they form a “path” to a function contained within an Ember object. In the case of the above button the “path” would be App.tweetsController.loadTweets() , the same function called when a user presses the Enter key within the text field. Load index.html in your browser and click the submit button, or press the Enter key within the input field. If you’re viewing the browser console you’ll see an error. This is becauseApp.tweetsController is not yet defined.

Preparing the Tweet storage object

Now would be a good time to define App.tweetsController . Add the following code after the Controllerscomment block in app.js. The code below should be familiar to you. Namespace, ArrayController, content array–it’s all there. This time though you’ll be adding an arbitrary property ( username ), and a function ( loadTweets ). After adding the ArrayController, reload your browser. Type a word in the input box and then click the button. You’ll get an alert box that echoes the word you typed. Feel free to remove the alert line at any time. You’ll also see an error indicating that the addUser method is not defined.

App.tweetsController = Em.ArrayController.create({ 
   content: [], 
   username: '', 
   loadTweets: function() { 
      var me = this; 
      var username = me.get("username"); 
      alert(username); 
      if ( username ) { 
         var url = 'http://api.twitter.com/1/statuses/user_timeline.json' 
             url screen_name=%@&callback=?'.fmt(me.get("username")); 
         // push username to recent user array  
         App.recentUsersController.addUser(username); 
    } 
  } 
});

Take a closer look at the loadTweets function definition; it has some unfamiliar bits. The first line sets a scope for the rest of the function. By definition, the scope or this for all Ember objects is the current function, in this caseApp.tweetsController . However, you’ll be adding more functionality to the loadTweets function later in this tutorial. Setting the current scope now helps Ember understand the context you’re using.

As I noted previously, Ember offers a number of helper functions to make writing applications easier, and these include get() and set() . These two functions are built into every Ember object and provide quick access to any property or function. The next line uses the scope of the current object, App.tweetsController , and then calls the get() function, passing in the name of the property that you wish to get a value for. You might be curious about where the value of username is coming from to begin with. Remember that Ember’s data bindings are bidirectional. This means that as soon as you type a value into the input field the valueBinding attribute of the input field view updates the App.tweetsController object with a value.

After the username has been retrieved, a test is run to make sure it’s not empty. At the moment there are only two statements within the if block, but that will change later. The first statement sets the URL to Twitter’s JSON file for a user. You might not immediately notice anything special about this until you look closer and see %@ , and the.fmt() at the end. The .fmt() function performs a handy string replacement with the %@ as the marker. Since the design of the application calls for storing a running list of searches, you’ll have to somehow store your search term. The final line performs that function, pushing the username value into the App.recentUsersControllerArrayController. Since this object doesn’t exist yet, running the code will result in an error.

Storing previous searches

In this next section you’ll create the object used to store recent searches. Take the following code and add it after the App.tweetsController object.

App.recentUsersController = Em.ArrayController.create({ 
   content: [], 
   addUser: function(name) { 
      if ( this.contains(name) ) this.removeObject(name);
                this.pushObject(name);
        },
        removeUser: function(view){
                this.removeObject(view.context);
        },
        searchAgain: function(view){
                App.tweetsController.set('username', view.context);
                App.tweetsController.loadTweets();
         },
         reverse: function(){
                return this.toArray().reverse();
         }.property('@each') });

You’re already familiar with creating an ArrayController and adding an empty content array, but this object has a few new elements starting with the addUser function. This will check the existing array ( this ) using a built-in Ember function named contains() . If it finds a result it removes it by using the ArrayController’s functionremoveObject() . This function has an opposite named pushObject(), which is used to add individual objects to the content array. Both functions also have pluralized versions that handle multiple objects: pushObjects()and removeObjects() . This code first removes an existing term before adding it so that the same search term isn’t displayed more than once. Since you already know how to remove an object from the content array, the only new element in the removeUser() function is the argument. When a function is called using the {{action}}helper, Ember implicitly passes in a reference to the current view. In the case of App.tweetsController , the view has a context that is essentially the item that is currently being iterated over. This context is used to remove the selected item from the array.

The searchAgain() function also receives the current view as an argument. When a user clicks a previously searched term, this function populates App.tweetsController.username with the selected username, then triggers the loadTweets() function, offering a single-click view for previous searches.

By default, Ember displays contents to the page in ascending order. Array index 1 is first, array index 2 is second, and so on. The design of this application calls for displaying recent searches in descending order. This means that the array must be reversed. While this isn’t built-in functionality you can see how easy it is to add. Reverse() first converts the Ember content array into a plain vanilla array using the Ember toArray() function, reverses it, and then returns it. What makes it possible to use this function as a data source is the property() function tacked on at the end. The property() function takes a comma-delimited list of properties required by the specified function. In this case the property() function is implicitly using the content array itself, addressing each element within that array using the @each dependant key. You’ll see how to implement the reverse() function in the next section.

Displaying previous searches

Now that you’re storing your previous searches, it’s time to display them on the page. Copy the following template and add it after the h3 tag labeled Recent Users .

<ol> 
   {{#each App.recentUsersController.reverse}} 
      <li> 
         <a href="#" title="view again" {{action "searchAgain" target="App.recentUsersController"}}>{{this}}</a> -     
         <a href="#" title="remove" {{action "removeUser" target="App.recentUsersController"}}>X</a> 
      </li> 
   {{/each}} 
</ol>

You should be familiar with all of this code at this point. The each block points at the content array and the HTML contained within it will be applied for every item within the App.recentUsersController variable. It’s not necessary to explicitly point to the content array, but in this case this code points to the reverse function, which provides the data in reverse order. The {{action}} helper lets users click on each anchor tag and trigger the indicated function. The only element that might not be familiar is {{this}} . When iterating over a content array, Ember keeps a reference to the current index in the {{this}} variable. Because the value of each item is only a string, you can directly output the value of the current item using {{this}} . Clicking on a Twitter username will load that user’s tweets again, while clicking on their name will remove them from the recentUsersController .

Loading tweets

Saving search terms is good, but how about actually performing the search? Next you’ll be adding the pieces that will retrieve the JSON packet from Twitter and display it to the page. Take the following Ember Model and add it directly after the comment block labeled Model. Remember that Ember Models are a blueprint for the data they will contain.

   App.Tweet = Em.Object.extend({ 
   avatar: null, 
   screen_name: null, 
   text: null, 
   date: null 
});

In app.js locate the line that reads App.recentUsersController.addUser(username); and add the following code directly after it:

$.getJSON(url,function(data){ 
   me.set('content', []); 
   $(data).each(function(index,value){ 
      var t = App.Tweet.create({ 
         avatar: value.user.profile_image_url, 
         screen_name: value.user.screen_name, 
         text: value.text, 
         date: value.created_at 
      }); 
      me.pushObject(t); 
    }) 
});

If you’ve used jQuery before you might have used the .get() function to retrieve data. The .getJSON() function does the same thing except it expects a JSON packet as a result. In addition it takes the returned JSON string and converts it into executable JavaScript code for you. Once the data has been retrieved, the content array is emptied removing all existing tweets. The next line takes the packet of data and wraps it in a jQuery object so that the.each() method can loop over the resulting Tweets. Within the each block a copy of the Tweet Model is populated with data, and then pushed into the ArrayController.

Finally, you’ll need to add the following display code to index.html. Copy and paste it directly after the h3 tag labeled Tweets .

<ul> 
   {{#each App.tweetsController}} 
      <li> 
         <img {{bindAttr src="avatar"}} /> 
         <span>{{date}}</span> 
         <h3>{{screen_name}}</h3> 
         <p>{{text}}</p> 
       </li> 
    {{/each}} 
</ul>

Ember makes it easy to output data to the page using plain {{Handlebars}} but there’s a catch. Remember how Ember wraps outputted values in script tags? That’s not an option when you’re working with HTML attributes. So Ember provides the {{bindAttr}} helper. Any attribute placed within this helper will output as normal, but still retain bindings. Go ahead and run your application now. Input a username and watch the Tweets fly in.

Where to go from here

In this article you learned the basics of Ember.js functionality. You learned how Ember implements MVC using its Models, Views, Controllers, and of course the Application object. You created templates with view helpers and action helpers using Handlebars. You learned how to create blueprints for your data using Models, store that data in collection sets with Controllers, and display the data to the page using Views. Finally, you used Ember to build an entire application with data bindings, computed properties, and auto-updating templates. Your mother would be so proud of you!

For further reading on Ember, check out a few of the following links:

 

Comments

comments

Powered by Facebook Comments

3 Comments

  1. jadeye
    December 8, 2012

    Hi,

    Thank you for a wonderfull tutorial.

    I’ve been following it and encountered a little error.

    The line of code on the “Preparing the Tweet storage object” paragraph need to be:

    url += ‘?screen_name=%@&callback=?’.fmt(me.get(“username”));

    Thank you,

    jadeye

    Reply
  2. Joe Larson
    February 15, 2013

    This is a very helpful article — Ember’s site really needs a concise tutorial like this. I was especially excited that I was able to immediately add a few features without thinking too hard.

    I have some questions (if you email me that would be awesome!):

    1) Is there a reason you did not use lead uppercase for App.tweetsController?

    2) Assuming that in a real app you break up your JS into multiple files, what kind of directory structure do you like to use?

    3) Can you post a zip with all the code?

    4) Can you fix the errata noted above?

    5) 3 space indendation? Seriously? Sorry, just had to comment ; )

    thanks so much!

    Reply
  3. Joe Larson
    February 15, 2013

    Oh yeah, also:

    6) Do you use any form of the module pattern or require.js or anything like that in your Ember projects, and if so what does that look like?

    Reply

Leave a Reply