Navigation

Why I recommend EmberJS over AngularJS

tldr: scroll down and read the headlines and bold words to get a quick overview.

For the past couple years I have absolutely loved developing web applications with AngularJS. Angular has made it so easy to provide the advance user experience that people are coming to expect. And as a developer I have enjoyed how easy Angular has made two way databinding and unit testing the JavaScript code with its built in dependency injection.

Everything was awesome!

But then the Angular team announced their plans for Angular 2.0 and everything was no longer awesome.

When the Angular team announced version 2.0 would be a complete rewrite, I felt like a good friend had just abandoned me.

Although the Angular team said there would eventually be an upgrade path, I didn’t believe them. A framework rewrite almost always means rewriting apps that use the framework.

(3/10/2015 Update: Last week during NgConf the Angular team proved me wrong by sharing it will be possible to incrementally upgrade an app to 2.0 without rewriting everything at once. I must say, I am impressed. It still doesn’t change my recommendation for Ember over Angular, but I am happy to hear about it.)

I was upset. So I started looking at other JavaScript frameworks. I wanted to make sure there wasn’t another framework out there that was competitive with Angular.

And that’s when I found the EmberJS framework.

Ember has been out for a couple years now, and I have been hearing a little bit about it here and there from other JavaScript developers online, but I had never spent any time looking into it. I saw that CodeSchool had a course on it, so I signed up for the course and starting going through it.

Learning Ember

As I was learning Ember, I realized right away that Ember was not Angular, and it had its own way to structure a JavaScript application. Unlike Angular, Ember is a very opinionated framework which forces you to structure your app the way it wants you to. And if you don’t follow its guidelines, then you’ll quickly find yourself fighting against the framework.

Honestly, I was kind of annoyed with how opinionated Ember was, but then something happened that completely changed my mind on the matter. Let me share.

About six hours into learning Ember, I decided to take a break from the course and take a look at the source code for a production app that was using Ember. I knew the Discourse forum software was open source and built with Ember. So I found its Github page and started browsing its Ember code.

I opened a template file and looked through it. I saw that it was displaying a value from a property, so I opened the controller file associated with the template. I saw the property was actually a computed property based upon the controller’s model. Next, I browsed to the controller’s associated route and saw how the controller’s model was retrieved. I then went back to the template file and saw that a view helper was being used, so I opened the view helper file to see what it was doing.

And then I distinctly remember pausing and thinking something didn’t feel right. Something was off, but I couldn’t put my finger on it. And then it hit me: I was actually understanding the code base for a project I had never looked at before.

For a project the size of Discourse, it typically takes a few hours to a few days to just begin to understand its code base. But Ember’s strong opinions and conventions allowed me to open a project I had never seen before and understand what was going on in only a few minutes.

And that’s when my opinion about Ember changed. Up until that point I had been hesitant about Ember and didn’t think it was better than Angular. But that experience browsing the source code of a production Ember app helped me begin to understand Ember’s benefits over Angular.

I then I began learning all I could about Ember. And the more I learned about Ember and its community, the more I appreciated Ember. And now I fully recommend Ember over Angular. Let me share my reasons why.

Why I Recommend Ember over Angular

My reasons for recommending Ember over Angular are not the typical reasons given by developers when they recommend a framework. But once you read my explanation for each reason, I hope you’ll also see the benefits Ember has.

But before I share my reasons for Ember, I want to share some reasons that did not affect my decision.

Reasons that did not affect my decision

It doesn’t have new functionality

Anything you can do in Angular, you can do in Ember. And anything you can do in Ember, you can do in Angular. Both frameworks can provide the same functionality to the end user.

To be sure, how you implement a feature (like a modal, or sorting a table) is different in Ember versus Angular, but the end result is the same. The user would notice no difference if the website was built with Ember or Angular. So difference in functionality is not a reason for my Ember recommendation.

It’s not the next shiny new toy

It could be argued that I have the Shiny New Toy Syndrome because every couple of years I’m advocating for a new JavaScript framework. It started with Backbone, then a couple of years later I started advocating for Angular, and now it’s Ember.

I would argue I don’t have the shiny new toy syndrome, though, because each framework I have advocated for had benefits over the previous framework of choice.

The reason I have changed my recommended JavaScript frameworks so much is because the JavaScript framework ecosystem itself is still in its infancy and has not matured yet. It seems every week there is a new JavaScript framework created.

It’s not cool to change frameworks just because there is a new framework out. There must be a business reason to change. And as you’ll read below, I would argue there are some very good business reasons to move to Ember.

Reasons that did affect my decision

The Core Team

When deciding between frameworks, the members of the core team usually don’t affect my decision making process. But this time it did affect my decision to choose Ember over Angular. And actually, it was one of the main deciding factors for me.

Here’s why: every member on Ember’s core team is actively working on one or more production apps that use Ember.

Let me explain why this is so important to me.

When everyone on the core team has their own production apps using the framework, then the core team is forced to experience all the joys and pains of using the framework, just like every other developer does. And this means the core team will naturally look out for the good of all developers using the framework.

On the flip side, when the core team members don’t use the framework on production apps, then “the sky is the limit!” It’s easy to forget about the developers using the framework as they dream up new features. Framework designers don’t feel pain in brainstorming meetings.

Angular 2.0 is a perfect case in point for this situation. One reason they are going forward with a complete rewrite of the framework is because the core team members will not experience the pain of rewriting an app to migrate from version 1.x to 2.0. But you can be sure the Ember team won’t do the same thing because they also would have to rewrite their apps.

Another example of what happens when the core team dogfoods their own framework is Ember CLI. The main reason the core team is developing Ember CLI is because they not only see its benefit but also experience its benefit. I’ll go into more detail on Ember CLI below.

Like I mentioned, I haven’t heard many others look at the members of the framework’s core team, and use them as a reason for choosing one framework over another. But to me, it’s one of the most significant reasons for my decision.

Guidance is set by those with the most experience

So here’s a question: Who do you want to set the guidance and best practices for using a framework? Developers with some experience using the framework or developers that actually created the framework?

That’s the difference between Angular and Ember.

Angular doesn’t provide much guidance on how to use the framework, so it becomes the wild west when a company first adopts Angular. Every team uses the framework however they see fit. But eventually it does come to a head when the company realizes a developer can no longer easily switch teams because the framework is used so differently between teams. Then a few developers from different teams get together and come up with the company’s official guidance and best practices for using the framework.

But then that guidance only applies to apps created going forward. The apps that are already using the framework are grandfathered in because it would take too much time to refactor them. So it’s still difficult for developers to switch teams because there’s still legacy apps. And the developers that created the guidance only went off their own experience and features they had to create. They didn’t experience all the edge cases, so the guidance is in constant flux as new scenarios are coming up.

Here’s a case in point: there are four development teams currently using Angular at my work place, and every team has structured their Angular app differently. There is no consistency even though we’re all using the same framework. And we have literally spent multiple days worth of time discussing and debating how our apps should be structured. And they are still not structured the same way.

Ember, on the other hand, has its guidance and best practices set by its core team members. And these guidances and best practices are baked directly into the framework, so it’s easy to follow.

Because Ember specifically defines how an application should be structured, the developers do not waste hours upon hours debating how an app should be structured. Instead they can spend that time actually developing the app and providing value to the business.

It also means new team members that already know Ember can become productive in any Ember app within minutes because they know how the app is structured and where to go to add a new feature or fix a bug. Like I shared above, after only spending a few hours learning Ember, I was able to browse and understand the structure of a large Ember application. That can’t be said for most developers learning Angular.

Use 2.0 features in the 1.x version series

Many new features are being added for Ember version 2.0, yet when version 2.0 is finally released, it won’t contain any new features. I know, it doesn’t make sense. It didn’t make sense to me at first either. But after I understood Ember’s versioning process it finally did make sense to me. And it turns out Ember’s versioning process is pretty cool.

The reason no new features will be introduced when Ember 2.0 is released is because all the 2.0 features will have already been introduced in the 1.x version releases.

The Ember team has a lot of new features they want to add to Ember 2.0, but they’re not waiting for 2.0 to implement them. They’re introducing the 2.0 features in version 1.x as they complete them. For example, that means Ember apps can take advantage of using the react style virtual DOM for increased speed, whereas Angular developers need to wait until Angular 2.0 comes out.

Another cool feature being added to Ember is Fast Boot. This allows an Ember app to serve HTML that’s already populated with data when a user first comes to the site. This means the user doesn’t have to wait for all the JavaScript files to be parsed and the initial AJAX calls to be made before they can see the data on the page. It’s all there in the initial HTML that is served up. Then after the JavaScript files are parsed, the page turns into a normal Single Page App (SPA).

So if nothing new is being introduced when Ember 2.0 is releaseed, what actually changes when it’s released? Basically the deprecation warnings in 1.x go away and if you had any deprecation warnings, then that code no longer works.

Ember CLI

Ember CLI is a game changer for Ember. And it will make all other JavaScript frameworks absolutely jealous. It not only streamlines developing Ember apps, but it also makes it super easy to share common code among different Ember apps. And all this results in making Ember developers more productive.

Ember CLI takes care of all the grunt work that’s involved in adding new third party libraries and code to the app.

For example, let’s say you want to add a date picker to your Ember app. With Ember CLI, all you have to do is execute the following command and you can immediately use the datepicker addon:

  1. ember install:addon ember-cli-datepicker

Now compare that with the typical set of steps required to install an addon without using Ember CLI:

  1. Download the addon (typically with bower or npm)
  2. Update the build config to include the addon’s javascript
  3. Update the build config to include the addon’s css
  4. Update the app’s dependencies to include the addon

As you can see, Ember CLI combines these four steps into a single command. And sure, these four steps only take a minute or two to perform manually, but that time quickly adds up over the lifetime of developing an application.

Another major feature in Ember CLI is blueprints. These are snippet generators that automatically add the scaffolding code required when creating new features in your Ember app.

Here’s an example of using Ember CLI to create a new route named foo:

ember generate route foo

installing
  create app/routes/foo.js
  create app/templates/foo.hbs
installing
  create tests/unit/routes/foo-test.js

So with one command, not only are all the necessary files created, but each file is stubbed out with the necessary scaffolding code and ready for you to actually write your application specific logic.

For example, here is the contents for the test/unit/routes/foo-test.js file that was generated:

import {
  moduleFor,
  test
} from 'ember-qunit';

moduleFor('route:foo', 'FooRoute', {
  // Specify the other units that are required for this test.
  // needs: ['controller:foo']
});

test('it exists', function() {
  var route = this.subject();
  ok(route);
});

And now you can focus on writing your tests instead of spending a minute or two writing the plumbing code required before you can start testing.

No other JavaScript framework I know of has a command line utility like Ember CLI. So while developers on other JavaScript frameworks spend time writing plumbing code and updating their builds when adding third party libraries, Ember developers have that much more time to actually write their apps.

Conclusion

Don’t get me wrong, I believe Angular 2.0 will be great, and I have enjoyed writing web applications in Angular 1.x. The complete rewrite for Angular 2.0 has really turned me off from the framework, though, and it made me realize there’s another framework out there that looks out for the developer more than Angular.

So I encourage you to give Ember a try. I think you’ll find it has some great benefits just like I did.

Debounce Search Filter in EmberJS

A common piece of functionality that I typically include in my web applications is the ability for users to quickly filter the data displayed on the page. A simple text box is often used to provide the filtering.

It’s pretty easy to bind a text box to a property on the controller and filter the results by the value in the text box. But this also means that the filter is performed after every keystroke the user enters. For small datasets this is fine, but for large datasets it can cause the UI to start locking up. So it’s better to wait until the user has finished entering the filter text before actually performing the filter.

I’m going to share three ways to perform filtering in Ember, starting with applying the filter immediately after each keystroke, and then go on to show how to apply the filter when the user clicks a button, and finally how to apply the filter after the user has stopped entering text in the filter text box.

Filter Immediately

Applying the filter immediately is straightforward in Ember. You just need a text box that’s bound to a property on the controller. And the controller will also have a computed property that returns the filtered results. This computed property will be re-calculated anytime the filter text changes.

Here’s a basic example of how that looks:

App.IndexController = Ember.Controller.extend({
  filterText: '',
  filteredResults: function() {
    var filter = this.get('filterText');
    return this.get('model').filter(function(item) {
      return item.toLowerCase().indexOf(filter) !== -1;
    });
  }.property('filterText')
});
<script type='text/x-handlebars' data-template-name='index'>
  {{input value=filterText type='text' placeholder='filter'}}
  <ul>
    {{#each item in filteredResults}}
      <li>{{item}}</li>
    {{/each}}
  </ul>
</script>

JS Bin Example

Filter on Demand

The easiest way to mitigate the filter being applied after every keystroke is to wait until the user clicks a button to apply the filter. In this scenario the filterText property on the controller will still be bound to the text box and will be updated with every keystroke. That means we can no longer apply the filter when the filterText property changes.

We’ll need to create an additional property called filter that keeps track of the current filter value. And the filteredResults computed property will now be dependent upon filter instead of filterText.

And the last change we’ll need to make is to add the applyFilter action to the controller. When this is called after the user clicks the button, it will copy the value from filterText to filter and cause the filteredResults to be re-computed.

App.IndexController = Ember.Controller.extend({
  filter: '',
  filterText: '',
  filteredResults: function() {
    var filter = this.get('filter');
    return this.get('model').filter(function(item) {
      return item.toLowerCase().indexOf(filter) !== -1;
    });
  }.property('filter'),
  actions: {
    applyFilter: function() {
      this.set('filter', this.get('filterText'));
    }
  }
});
<script type="text/x-handlebars" data-template-name="index">
  {{input value=filterText type='text' placeholder='filter'}}
  <button {{action 'applyFilter'}}>Apply Filter</button>
  <ul>
    {{#each item in filteredResults}}
      <li>{{item}}</li>
    {{/each}}
  </ul>
</script>

JS Bin Example

This does the job, but it doesn’t provide the best experience for the user because they have to click the filter button each time. What would be better is to wait until the user is done typing in the filter text box and then automatically apply the filter.

Filter on Pause

To wait to perform the filter until the user has finished entering the filter value, we’ll use the Ember.run.debounce function.

In case you don’t have much experience with a debounce function, let me explain how it works.

When you call debounce, you tell it what function you want called and how long it should wait to call the function. For example, you can tell debounce to call a function named updateQuotes in 5 seconds. And debounce will set a timer for 5 seconds and call updateQuotes when the timer has elapsed.

However, if debounce is called again before the timer completes, then the timer is reset and starts counting down again. And the function will not be called as long as debounce is called again before the timer completes.

We’ll use debounce’s functionality to automatically apply the filter after the user finishes entering the value they want to filter by.

Here is how that looks:

App.IndexController = Ember.Controller.extend({
  filter: '',
  filterText: '',

  onFilterTextChange: function() {
    // wait 1 second before applying the filter
    Ember.run.debounce(this, this.applyFilter, 1000);
  }.observes('filterText'),

  applyFilter: function() {
    this.set('filter', this.get('filterText'));
  },

  filteredResults: function() {
    var filter = this.get('filter');
    return this.get('model').filter(function(item) {
      return item.toLowerCase().indexOf(filter) !== -1;
    });
  }.property('filter')

JS Bin Example

Using Ember Data with Asp.Net Web API

Last week I started playing around using EmberJS with an Asp.Net Web API backend. And one of the first issues I ran into was getting Ember and Asp Web API to actually communicate with other.

But the first time I tried to retrieve data from the server, Ember Data threw the following exception:

Error while processing route: posts Assertion Failed: You must include an `id` for App.Product in an object passed to `push`

What was going on? After some quick reading through the Ember Data documentation, I realized Ember Data expects the JSON data returned from the server to be in a different format than Asp.Net Web API was returning.

Ember Data expects the JSON data to and from the server to look like this:

{
  posts: [{
    id: 1,
    title: 'First Post'
    publishDate: '2014-11-07'
  }, {
    id: 2,
    title: 'Second Post'
    publishDate: '2014-11-14'
  }]
}

But Asp.Net Web API expects the JSON data to look like this:

[
  {
    Id: 1,
    Title: 'First Post',
    PublishDate: '2014-11-07'
  }, {
    Id: 2,
    Title: 'Second Post',
    PublishDate: '2014-11-14'
  }
]

Notice there are two main differences here between the data formats. First, Ember Data expects a root node named posts to contain the array of posts. But the Web API just returns the array of posts. And second, Ember Data expects the property names to be camelCased. But the Web API has PascalCased property names.

Fortunately Ember Data makes it easy to massage the data that’s coming from and being sent to the server. This means you don’t have to change the default way that Asp.Net Web API serves data.

Massage the data with Serializers

Ember Data uses objects called Serializers to massage and map the data format from the server to the format Ember expects (and vice versa).

Ember includes a few default Serializers for you in its framework that you can choose to use. There’s the JSONSerializer and also the RESTSerializer that extends the JSONSerializer. The RESTSerializer provides most of the mapping we already need. So we will extend it and will only need override a couple functions to perform our specific mapping.

Let’s first map the data coming from the server into the format Ember expects. We will do this by overriding the extract method.

App.ApplicationSerializer = DS.RESTSerializer.extend({
  extract: function(store, primaryType, payload, id, requestType) {
    ...
  }
});

You can see there are quite a few arguments for the extract method, but we are only concerned about two of them: the payload and the primaryType. The payload contains the raw response from the server, and the primaryType will tell us the name of the root node to include in the result.

Let’s first convert the PascalCased property names from the server to camelCased names that Ember expects.

extract: function(store, primaryType, payload, id, requestType) {
  var i, record, propertyName, value, newPropertyName;
  for (i = 0; i < payload.length; i++) {
    record = payload[i];
    for (propertyName in record) {
      value = record[propertyName];
      newPropertyName = propertyName.camelize();
      record[newPropertyName] = value;
      delete record[propertyName];
    }
  }
}

Notice we’re taking advantage of the camelize() function that Ember adds to all string objects. This function takes care of renaming the property to camelCase. So PublishedDate becomes publishedDate with the camelize() function.

Now we need to add the root element to the payload that Ember is expecting.

{
  //root node
  posts: [{...}]
}

And that means we need to retrieve the name for the root element. We can get that information from the primaryType argument. It has a property on it called typeKey that holds the value for the expected root element name.

extract: function(store, primaryType, payload, id, requestType) {
  //... previous code ...
  payloadWithRoot = {}
  payloadWithRoot[primaryType.typeKey] = payload

  this._super(store, primaryType, payloadWithRoot, id, requestType);
}

And the last thing we do is call the _super function to allow the default RESTSerializer to continue on with the normal extraction.

Before we move on, let’s refactor the code so it works with both arrays and single objects that are returned from the server.

App.ApplicationSerializer = DS.RESTSerializer.extend({
  extract: function(store, primaryType, payload, id, requestType) {
    var i, record, payloadWithRoot;
    // if the payload has a length property, then we know it's an array
    if (payload.length) {
      for (i = 0; i < payload.length; i++) {
        record = payload[i];
        this.mapRecord(record);
      }
    } else {
      // payload is a single object instead of an array
      this.mapRecord(payload);
    }
    payloadWithRoot = {}
    payloadWithRoot[primaryType.typeKey] = payload
    this._super(store, primaryType, payloadWithRoot, id, requestType);
  },
  mapRecord: function(record) {
    var propertyName, value, newPropertyName;
    for (propertyName in record.toJSON()) {
      value = item[propertyName];
      newPropertyName = propertyName.camelize();
      item[newPropertyName] = value;
      delete item[propertyName];
    }
  }
});

That’s all it takes to massage the data coming from the server. Now we need to massage the data that is sent down to the server from Ember. And fortunately this is much easier than the other way around.

To do this, we will override the serializeIntoHash function.

serializeIntoHash takes four arguments, but we’re only concerned about two of them. The record argument is the model that is to be saved to the database, and the hash argument is the actual JSON object that will be sent to the server.

So all we need to do is loop through the properties and values in the record object and copy them over to the hash object. And during the copy, we’ll transform the property name to PascalCase.

App.ApplicationSerializer = DS.RESTSerializer.extend({
  extract: function(store, primaryType, payload, id, requestType) {
    ...
  },
  mapRecord: function(record) {
    ...
  },
  serializeIntoHash: function(hash, type, record, options) {
    var jsonRecord, propertyName, value;
    jsonRecord = record.toJSON();
    for (propertyName in jsonRecord) {
      value = jsonRecord[propertyName];
      hash[propertyName.capitalize()] = value;
    }
  }
});

And as you can see, the Ember framework provides the capitalize() function on string objects, and it will take care of transforming the property names to PascalCase.

Conclusion

Ember Data has a bit of a learning curve to understand it. But I really appreciate the amount of thought that went into its design so that it can provide the needed flexibility to work with multiple server backends, including Asp.Net Web API.

Ember Data has a bit of a learning curve to understand it at first. But I really appreciate the amount of thought that went into its design. This is evident in how easy it is to work with multiple API backends, including Asp.Net Web API.

Complete Solution

Here’s the complete solution as a reference.

App.ApplicationSerializer = DS.RESTSerializer.extend({
  extract: function(store, primaryType, payload, id, requestType) {
    var i, record, payloadWithRoot;
    // if the payload has a length property, then we know it's an array
    if (payload.length) {
      for (i = 0; i < payload.length; i++) {
        record = payload[i];
        this.mapRecord(record);
      }
    } else {
      // payload is a single object instead of an array
      this.mapRecord(payload);
    }
    payloadWithRoot = {}
    payloadWithRoot[primaryType.typeKey] = payload
    this._super(store, primaryType, payloadWithRoot, id, requestType);
  },
  mapRecord: function(record) {
    var propertyName, value, newPropertyName;
    for (propertyName in record.toJSON()) {
      value = item[propertyName];
      newPropertyName = propertyName.camelize();
      item[newPropertyName] = value;
      delete item[propertyName];
    }
  },
  serializeIntoHash: function(hash, type, record, options) {
    var jsonRecord, propertyName, value;
    jsonRecord = record.toJSON();
    for (propertyName in jsonRecord) {
      value = jsonRecord[propertyName];
      hash[propertyName.capitalize()] = value;
    }
  }
});

AngularJS: Count the number of watchers on a page

One of the main benefits AngularJS provides is its two way databinding. It uses dirty checking to implement this, so whenever any value changes, then all the databound values are checked to see if they have also changed and updated accordingly. Fortunately the dirty checking is usually fast and it can be performed without any perceived lag from the end user (see this Stack Overflow post for a reference).

However, it’s still good to pay attention that a page does not contain thousands upon thousands of databound elements, though, because the user can start noticing a lag, especially from mobile devices. There is a Chrome extension called Batarang that can show you how many databound elements a page has, but it hasn’t been updated in over a year, and its recent reviews complain that the extension is broken for them. So I created a simple script that counts the number of databound elements on the page.

AngularJS keeps track of two way databound elements by adding a watcher function to the $$watchers array on the scope. So to find out how many dirty checks AngularJS performs on the page, we just need to count the number of watchers on every scope.

To do this, the first thing we need to do is get a reference to all the scopes on the page. This can be done by taking advantage of the fact that AngularJS adds the ng-scope class to every element that is associated with a scope. And then we can call angular.element(element).scope() on each element to retrieve a reference to the actual scope itself. Here is a code example showing how to get the scope from a single element on the page.

var elementsWithScope = document.querySelectorAll('.ng-scope');
var firstElementWithScope = elementsWithScope[0];
var scope = angular.element(firstElementWithScope).scope();

And then we can loop through each scope and count the number of watchers.

Here is what the final code snippet looks like:

countWatchers = function() {
  var watchers, elementsWithScope, scope, i, len;
  watchers = 0;
  elementsWithScope = document.querySelectorAll('.ng-scope');
  for (i = 0, len = elementsWithScope.length; i < len; i++) {
    scope = angular.element(elementsWithScope[i]).scope();
    if (scope.$$watchers != null) {
      watchers += scope.$$watchers.length; 
    } 
  } 
  return watchers;
};

And once you include this JavaScript in you AngularJS site, you can open up the browser console on any page and type countWatchers(), and it will return the number of watchers on the page.

Bookmarklet

When I shared this code snippet with my co-worker, Eric Guthmann, he mentioned that it could easily be made into a bookmarklet so that it can be used on any AngularJS site. And then there wouldn’t be the need to manually include a JavaScript file with the website itself. So with his help, I created the bookmarklet below.

You can save the bookmarklet below by dragging and dropping it on your bookmark bar.

AngularJS Watcher Count

Test an AngularJS Directive with Protractor

When you write an awesome AngularJS directive, you want to share it with the world. So you put it in its own repository on Github and now everyone else can use it. And because others are using your directive, you want to make sure it’s well tested. Fortunately that’s easy to do with Karma, but what if Karma is not enough to run all the test scenarios? What if you need to use Protractor to test some UI interactions with your directive?

It’s not difficult to use Protractor for your UI tests on a full fledged application, but now that you have separated out and isolated a single directive in its own repository, you no longer have a full fledged application to test against. You only have a single directive. You don’t even have a simple html page to display the directive in.

I ran into this same issue myself a few weeks ago, and after a lot of trial and error I was finally able to figure out how to run Protractor tests against a standalone directive that’s in its own repository. So I’ll share my findings to make it easier for others dealing with the same situation.

Create the smallest AngularJS website

Before you create your first Protractor test, you first need to get your directive to run within a website. So create the smallest website possible with the least amount of JavaScript code needed to run your directive. That typically means creating a single html file that references AngularJS and your directive along with including the html element that initializes the directive.

Here’s an example directive to show you what I mean. This directive just toggles a class on an element when it is clicked.

angular.module('click-toggle', []).directive(
'clickToggleClass', function() {
  return function(scope, elem, attrs) {
    var classToAppend = attrs['clickToggleClass'];
    var classAppended = false;
    elem.on('click', function() {
      classAppended = !classAppended;
      elem.toggleClass(classToAppend, classAppended);
    });
  });
});

And here’s the html file that initializes an AngularJS app and uses the directive.

<html>
<head>
  <script src='angular.js'></script>
  <script src='click-toggle-class.js'><script>
</head>
<body ng-app='click-toggle'>
  <div class='alert' click-toggle-class='alert-info'>Click Me to Toggle the class</div>
</body>
<html>

Serve up the website using ExpressJS

Now we need to serve up the html page through a website. It’s not enough to just open the html directly from the filesystem because Protractor won’t work.

There’s plenty of web frameworks to choose from to serve up an html page through a website, but most of them are overkill because we only have a single html file and a couple JavaScript files that need to be served. So let’s use a minimalist approach and have ExpressJS serve up these files.

ExpressJS is a micro web application framework for NodeJS, and it makes it super simple to serve up files on your computer inside a website. For example, let’s say you have the following directory structure, and you want to serve up the index.html file at http://localhost:3000/index.html

app.js
/public
  index.html
  click-toggle-class.js
  angular.js

The app.js file is the configuration file for the ExpressJS web server. And since we’re only serving up static files, we only need 4 lines of code in our app.js file

var express = require('express');
var app = express();
app.use('/', express.static(__dirname + '/public'));
app.listen(3000);

Now install ExpressJS and startup the server with the following command:

# install expressjs
npm install express

# run the expressjs server
node app.js

And you can now view the html page from your browser at http://localhost:3000/index.html

Run the Protractor tests

And finally we’ll create our protractor tests and run it against the directive.

// click-toggle-class_spec.js

describe('click-toggle-class', function() {
  it('adds the class on click when it did not previously have the class', function() {
    browser.get('');
    var el = element(By.css('div'));
    el.click()
    expect(el.getAttribute('class')).toBe('highlight');
  });
  it('removes the class on click when it previously had the class', function() {
    browser.get('');
    var el = element(By.css('div'));

    // first add the class by performing a click
    el.click()
    expect(el.getAttribute('class')).toBe('highlight');

    // now click again and confirm the class has been removed
    el.click()
    expect(el.getAttribute('class')).toBe('');
  });
});

Our Protractor config file will look like this:

// protractor.conf.js

exports.config = {
  specs: [ 'click-toggle-class_spec.js' ],
  capability: { browserName: 'chrome' },
  baseUrl: 'http://localhost:3000/index.html'
};

And we can now run our Protractor tests with the following command:

protractor protractor.conf.js

Conclusion

As you can see, it’s not completely straightforward to run Protractor tests against a standalone directive. But with the help of ExpressJS and creating a minimal website to run the directive, it is possible.

Add Comma Separated Thousands to Number Inputs in AngularJS

AngularJS provides the number and currency filters to automatically format numbers with comma thousands separators. But these filters don’t work when you want to format a number within an input text box.

Ideally you will want to show the comma thousands separators to start with, and remove them when the input box gains focus. I couldn’t find any directive that already did this, so I created the fcsa-number directive to provide this functionality. I also added a few validations that can be applied to it as well.

Here’s a few examples of what the directive does and the validations it provides:

If you want to use it on your site, just follow the installation instructions in the Readme file on the project’s Github site.

Validate and Format Numbers in AngularJS

When I want to provide a number input box with basic validation and formatting in an AngularJS app, I have found the default way to be lacking. The default way to is to declare an input element with a number type: <input type="number" />. However, I want to automatically add commas as thousands separators, and I want to validate no more than 2 decimals are entered for currency fields. Both of these are not possible with the the default way.

So I created the FCSA Number directive that provides this functionality. (view on Github)

Here’s a quick demo of the available validations and formatting provided by the directive.

To use it on your site

Install it on your site either with bower: bower install angular-fcsa-number

Or install it manually by copying src/fcsaNumber.js from the Github project into your project.

And make sure to reference the src/fcsaNumber.js in your JavaScript files.

Then add the fcsa-number module as a dependency to you Angular app.

angular.module('yourApp', ['fcsa-number']);

And finally add the fcsa-number attribute to textboxes you want to have validated and formatted with thousands separators.

<input type='text' ng-model='company.employeeCount' fcsa-number />

When an invalid number is entered by the user, the form and control will become invalid and the fcsa-number-invalid class will be added to the text box.

Run Protractor Tests in Parallel

My team has been using Protractor for our end to end tests for a couple months now, and the amount of time the test suite takes to finish has grown as we add more tests. So we started looking for ways to speed our tests up. We already have a Selenium grid running with a few browser instances connected to it. Ideally we would like to split the tests out to be ran in parallel among the browser instances in the grid.

The first configuration setting we tried was setting the count within the capabilities section of the configuration file.

exports.config = {
  capabilities: {
    browserName: 'chrome',
    count: 2
  },

  /* ... more protractor config values ... */
}

We ran Protractor and noticed both browser instances ran the tests. But after closer examination, we realized each browser instance was running all the tests. So the tests were being ran in parallel, but it wasn’t saving us any time because each test was ran twice.

I then dug into the source code for protractor in Github and found an example configuration file with comments about each setting. The comment for the count setting confirmed what we were seeing: ‘Number of times to run this set of capabilities (in parallel, unless limited by maxSessions)’. I continued reading the example configuration file and found what I was looking for right below the count setting.

The comment for shardTestFiles reads: ‘If this is set to be true, specs will be sharded by file (i.e. all files to be run by this set of capabilities will run in parallel). Default is false.’

So then I updated my config to the following and ran Protractor again.

exports.config = {
  capabilities: {
    browserName: 'chrome',
    shardTestFiles: true
  },

  /* ... more protractor config values ... */
}

However, this time only one browser instance ran the tests. They were not sharded between the 2 browser instances connected to the Selenium grid.

I went back to the example config file and then found the maxInstances setting, and read its comment that said this is only needed if shardTestFiles is true. So I added maxInstances to the config and ran Protractor again.

exports.config = {
  capabilities: {
    browserName: 'chrome',
    shardTestFiles: true,
    maxInstances: 2
  },

  /* ... more protractor config values ... */
}

And this time the test files were successfully sharded between the 2 browser instances.

Our tests were taking around 3 minutes to complete when they weren’t split up, and then they took around 2 minutes to complete after we split them among 2 browsers. So we weren’t able to realize a 50% reduction in the overall test time by splitting the tests between two browsers, but our overall test time was significantly improved.

PS

The shardTestFiles configuration was introduced in version 0.24. So if you’re running an earlier version then your test files will not be sharded.

jQuery UI images not showing in Rails 4

I recently upgraded an app to rails 4 from version 3.2 and noticed the images for the jQuery UI widgets were no longer showing in the production environment. They still showed up when I was in the development environment though. I did some googling and found out the reason the images are no longer showing is because Rails 4 no longer precompiles images in the assets folder.

I found a couple solutions on Stack Overflow to fix the issue, but they both required making a config change and manually updating the css file for jQuery UI.

Fortunately I found an easier solution that does not involve making any changes to the config nor to the css file.

The jQuery UI files were in the vendor assets folder, and the file structure looked like this:

/vendor
  /assets
    /stylesheets
      /jquery-ui
        /images
        jquery-ui.css

All I did was move the images folder from vendor/assets/stylesheets/jquery-ui/images to public/assets/jquery-ui/images

/public
  /assets
    /jquery-ui
      /images

And now the jQuery UI images are showing again.

Chaining Promises to Cleanup Your Angular Codebase

I’ve been using promises in JavaScript for a while now and thought I knew almost everything about them. But yesterday a co-worker showed me how it’s possible to manipulate the resolved value from one promise to another when they are chained together. Let me show you what I mean.

Below is a chained promise that logs the name to the console. It’s pretty straight forward.

getName().then(function(name) {
  console.log(name);
}).then(function(name) {
  console.log(name);
});

> 'Paul'
> 'Paul'

Now let’s have the first then function append a last name and return it. And now notice the output of the second log statement:

getName().then(function(name) {
  console.log(name);
  return name + ' Yoder';
}).then(function(name) {
  console.log(name);
});

> 'Paul'
> 'Paul Yoder'

In the first example, both then functions received the same resolved value from the original promise. But in the second example, the first then function returned a different value, and the second then function received that different value instead of the original value from the promise.

Applying Chained Promises to Angular

I was able to clean up a lot of my Angular code after finding out about this feature for chained promises. Here’s one example of how I was able to clean up the code.

Let’s say you have a REST API that returns information about a specific company. And the return value looks something like this:

{
  companyName: 'ACME',
  phone: '402-555-5555',
  employeeCount: 100
}

And you have an Angular service that consumes the REST endpoint and returns the employee count for the company. And it looks something like this:

angular.module('app').factory('employeeCountService', function($http, $q) {
  return {
    getByCompanyId: function(companyId) {
      var deferred = $q.deferred()
      $http.get('/companies/' + companyId).then(function(companyInfo) {
        deferred.resolve(companyInfo.employeeCount);
      });
      return deferred.promise;
    }
  };
});

// Here's a controller that uses the companyEmployeeCount service
angular.module('app').controller('HomeCtrl', function($scope, employeeCountService, companyId) {
  employeeCountService.getByCompanyId(companyId).then(function(employeeCount) {
    $scope.employeeCount = employeeCount;
  });
});

Notice that $q.deferred was used because we had to manipulate the returned value from the REST API and resolve the promise to only include the employee count.

By taking advantage of what we just learned about chained promises though, we can clean up the amount of code needed in the employeeCountService service to the following:

angular.module('app').factory('employeeCountService', function($http) {
  return {
    getByCompanyId: function(companyId) {
      $http.get('/companies/' + companyId).then(function(companyInfo) {
        return companyInfo.employeeCount;
      });
    }
  };
});

The controller code that uses employeeCountService did not change, and the getByCompanyId function no longer has to create its own deferred object. So there’s less code and it’s easier to understand, and I consider that a big win!