Which JavaScript Framework Is Right For You?

You’ve got the perfect idea for a JavaScript-based application or website. Now all you have to do is pick the right open source framework.

If you’re reading this, I’m assuming that you’re a beginner or novice developer, or else you’re hiring somebody else to develop your idea for you. But while you’ve probably heard the words Angular, Ember, and Backbone, you might not know what they are, or why they help web development.

Developers use JavaScript for lots of different Web applications because it can really make the client side—that is, the user’s side—look and work beautifully. But while it may not look old, the JavaScript language has been around for nearly 20 years, and it has the variety and vastness to prove it. If you keep adding more and more (see: redundant) code to make it work in multiple browsers and use cases, it can quickly become a big, confusing mess.

Enter JavaScript frameworks. Frameworks like Angular, Backbone, and Ember bring structure to your JavaScript code and keep it organized. They’re all open source, so they’re constantly being improved by the community. They also save you time because they’re each built on top of JQuery, a powerful library that makes some of JavaScript’s tricker operations easier to perform and more readable.

Here’s an example of how referencing JQuery might enhance the coding process: Instead of writing the tedious lines of code to generate a table, you could insert a reference to a JavaScript library that generates the table with just one line of code. Even better, these shortcuts don’t just help you work faster, they make your code work in multiple environments, too.

However, picking your JavaScript framework isn’t like picking between three different-colored T-shirts. In fact, it’s more like choosing between three completely different articles of clothing: Each piece helps to cover you, but they all function and look very differently. Carrying the metaphor even further, you wouldn’t wear a bathing suit in a cold climate, or a winter coat at the beach. Likewise, different JavaScript frameworks are better for different types of applications.

Here is a rundown of each of the three hottest frameworks, and what they’re best for:

AngularJS

Initially released in 2009, AngularJS is the oldest of the three frameworks. Probably as a result, it also has the largest community.

In 2013, Angular had the fourth-largest number of contributors and third-largest number of stars (kind of like Facebook “Likes”) on GitHub. On Built With AngularJS, you can check out all of the applications currently being developed with Angular.

Some of the most well-known companies that credit AngularJS as the JavaScript framework include Google and Nike. Beginning in August 2013, General Motors cars featureapplications that were built in Angular. You’ll also notice a lot of news sites using AngularJS on their front pages, like theGuardian, the Huffington Post, and MSNBC.

According to Igor Minar, lead developer on AngularJS at Google, it’s more about Angular’s adaptability than anything to do with the news.

“I don’t think that Angular is more suitable for news sites than for other sites and apps. But there definitely is a bunch of them,” Minar said. “I think it’s just that these are high-visibility sites maintained by companies in highly-competitive market which means they keep their technology stack fresh in order to be efficient at making changes and providing great user experience.”

Why are sites that use Angular good at making quick changes? Probably because Angular, more aggressively than any other framework, nudges developers to create easily testable code and test it often. Though some developers might find this guidance annoying, it pays off in catching little coding errors before they have a chance to become big ones.

Another peculiarity of Angular is that it has a lot of its own terms and jargon. Minar thinks this is because the framework includes some features no other JavaScript solution does.

“Some terms we use commonly are specific to Angular and might come across as jargon or strange,” he said. “The good news is that the web standards are catching up and giving ‘official’ names to some of these concepts.”

If you code with Angular, you’re coding on Angular’s rigid terms, but Google Trends points to that not being such a bad thing. You’ll have to use Angular’s jargon and it might take time to make your code more testable, but the result is adaptability later on.

Backbone.js

Backbone came out in June 2010, and its community is nearly as large as Angular’s.

Many popular applications use the Backbone framework, including Twitter, Foursquare, and LinkedIn Mobile. Also worth noting is that a number of music apps were built with Backbone, including Soundcloud, Pitchfork, and Pandora.

However, there’s something about Backbone that’s very, very small compared to other frameworks—and that’s its download size. Compressed and minified, AngularJS is about 36K; the Ember starter kit is even bigger, at 69K. But Backbone, compared to its contemporaries, is downright puny, at just 6.4K.

Backbone’s diminutive size is typically its biggest selling point, since it only depends on one JavaScript library instead of several. As a result, Backbone is extremely lightweight, which means it’s good for building fast and responsive applications—but it’s most effective if those web applications are themselves small and single-page, or better yet, only part of a page, like many of the music apps using Backbone right now.

Here’s another characteristic that makes Backbone special: Its framework is remarkably hands-off, or as some developers put it, Backbone doesn’t “hold your hand.” This means experienced JavaScript developers can quickly get started, but less experienced developers might find themselves writing a lot of “boilerplate,” or repetitive code.

According to Backbone creator Jeremy Ashkenas, concerns about needless boilerplate coding are “a silly marketing campaign.”

“If you’re writing a lot of ‘boilerplate’ code in Backbone, then you don’t know how to use it,” Ashkenas said. “In general, in programming, if you’re writing the same thing over and over again—you write a function to do it automatically for you.”

If you’re having trouble, however, Backbone has an especially active community rife with free tutorials for getting started with the framework. Plenty of developers have taken to GitHub to upload useful examples and how-tos that take the place of other frameworks’ hand-holding.

If you’re working on a single-page application or widget—and you’re comfortable with being a self-starter—Backbone is likely the lightweight framework for you.

Ember.js

Ember is the newest kid on the block, but it’s already making waves. Initially released in 2011, Ember just hit version 1.0 last year. It also recently become Code School’s latest course, and given that Code School already offers courses for Angular and Backbone, it’s likely the newest course will grow to become equally popular.

LivingSocial, Groupon, Zendesk, Discourse, and Square are some of the most well-known applications that have adopted Ember. Ember creators Tom Dale and Yehuda Katz say it’s easy to see when a site is using Ember because of its loading speed.

“They feel like normal websites, they’re just far faster than what you’re used to,” Dale said. “It’s because all the rendering happens in the browser. It may look like a regular website, but under the hood, it’s architected like an iOS or Android app that isn’t being rendered by the server.”

At 69K minified and zipped, Ember is the largest framework of the three, but Katz points out that often medium-sized jpegs are just as large.

“The reason I feel confident that the features we’re baking in are things you need anyway is because I frequently look at the compiled size of Ember apps alongside other apps in the wild, and they’re all roughly the same size,” said Katz, implying that developers who use other frameworks often download additional libraries and tools during the building process.

Ember’s larger library size partly explains why it’s the largest download of the three Javascript frameworks, but another reason is because Ember comes with a lot of built-in support for standard code features. If you’ve ever tried to click the “back” button on a website only to get no response, you know all about what happens when JavaScript applications break. Ember’s support features are there to keep small but annoying errors like those from happening.

Ember’s library size and support network are its two greatest strengths, but if you’re only trying to create a small widget or single-page app, it might be overkill for you. If you’re working on a multipage, navigational, long-term project, Ember might be your pick.

When developers discuss these three frameworks online among their contemporaries, the discussion often devolves into one of personal preference. But from a non-developer perspective, it’s clear that different applications—and different needs—make each framework shine its brightest.

Dependency Injection in Angular JS

Dependency Injection (DI) is a software design pattern that deals with how components get hold of their dependencies.

The Angular injector subsystem is in charge of creating components, resolving their dependencies, and providing them to other components as requested.

For in-depth discussion about DI, see Dependency Injection at Wikipedia, Inversion of Control by Martin Fowler, or read about DI in your favorite software design pattern book

DI in a Nutshell

There are only three ways a component (object or function) can get a hold of its dependencies:

  1. The component can create the dependency, typically using the new operator.
  2. The component can look up the dependency, by referring to a global variable.
  3. The component can have the dependency passed to it where it is needed.

The first two options of creating or looking up dependencies are not optimal because they hard code the dependency to the component. This makes it difficult, if not impossible, to modify the dependencies. This is especially problematic in tests, where it is often desirable to provide mock dependencies for test isolation.

The third option is the most viable, since it removes the responsibility of locating the dependency from the component. The dependency is simply handed to the component.

  1. function SomeClass(greeter) {
  2. this.greeter = greeter;
  3. }
  4. SomeClass.prototype.doSomething = function(name) {
  5. this.greeter.greet(name);
  6. }

In the above example SomeClass is not concerned with creating or locating the greeter dependency, it is simply handed the greeterwhen it is instantiated.

This is desirable, but it puts the responsibility of getting hold of the dependency on the code that constructs SomeClass.

To manage the responsibility of dependency creation, each Angular application has an injector. The injector is a service locator that is responsible for construction and lookup of dependencies.

Here is an example of using the injector service:

  1. // Provide the wiring information in a module
  2. var myModule = angular.module(‘myModule’, []);

Teach the injector how to build a greeter service. Notice that greeter is dependent on the $window service. The greeter service is an object that contains a greet method.

  1. myModule.factory(‘greeter’, function($window) {
  2. return {
  3. greet: function(text) {
  4. $window.alert(text);
  5. }
  6. };
  7. });

Create a new injector that can provide components defined in our myModule module and request our greeter service from the injector. (This is usually done automatically by angular bootstrap).

  1. var injector = angular.injector([‘myModule’, ‘ng’]);
  2. var greeter = injector.get(‘greeter’);

Asking for dependencies solves the issue of hard coding, but it also means that the injector needs to be passed throughout the application. Passing the injector breaks the Law of Demeter. To remedy this, we use a declarative notation in our HTML templates, to hand the responsibility of creating components over to the injector, as in this example:

  1. <div ng-controller=”MyController”>
  2. <button ng-click=”sayHello()”>Hello</button>
  3. </div>
  1. function MyController($scope, greeter) {
  2. $scope.sayHello = function() {
  3. greeter.greet(‘Hello World’);
  4. };
  5. }

When Angular compiles the HTML, it processes the ng-controller directive, which in turn asks the injector to create an instance of the controller and its dependencies.

  1. injector.instantiate(MyController);

This is all done behinds the scenes. Notice that by having the ng-controller ask the injector to instantiate the class, it can satisfy all of the dependencies of MyController without the controller ever knowing about the injector.

This is the best outcome. The application code simply declares the dependencies it needs, without having to deal with the injector. This setup does not break the Law of Demeter.

Dependency Annotation

How does the injector know what components need to be injected?

The application developer needs to provide annotation information that the injector uses in order to resolve the dependencies. Throughout Angular, certain API functions are invoked using the injector, as per the API documentation. The injector needs to know what services to inject into the function. There are three equivalent ways of annotating your code with service name information:

  • Implicitly from the function parameter names
  • Using the $inject property annotation
  • Using the inline array annotation

These can be used interchangeably as you see fit and are equivalent.

Implicit Dependencies

The simplest way to get hold of the dependencies, is to assume that the function parameter names are the names of the dependencies.

  1. function MyController($scope, greeter) {
  2. // …
  3. }

Given a function the injector can infer the names of the service to inject by examining the function declaration and extracting the parameter names. In the above example $scope, and greeter are two services which need to be injected into the function.

While straightforward, this method will not work with JavaScript minifiers/obfuscators as they rename the method parameter names. This makes this way of annotating only useful for pretotyping, and demo applications.

$inject Property Annotation

To allow the minifers to rename the function parameters and still be able to inject right services, the function needs to be annotated with the $inject property. The $inject property is an array of service names to inject.

  1. var MyController = function(renamed$scope, renamedGreeter) {
  2. }
  3. MyController[‘$inject’] = [‘$scope’, ‘greeter’];

In this scenario the ordering of the values in the $inject array must match the ordering of the arguments to inject. Using above code snippet as an example, $scope will be injected into renamed$scope and greeter into renamedGreeter. Care must be taken that the$inject annotation is kept in sync with the actual arguments in the function declaration.

This method of annotation is useful for controller declarations since it assigns the annotation information with the function.

Inline Array Annotation

Sometimes using the $inject annotation style is not convenient such as when annotating directives or services defined inline by a factory function.

For example:

  1. someModule.factory(‘greeter’, function($window) {
  2. // …
  3. });

Results in code bloat due to needing a temporary variable:

  1. var greeterFactory = function(renamed$window) {
  2. // …
  3. };
  4. greeterFactory.$inject = [‘$window’];
  5. someModule.factory(‘greeter’, greeterFactory);

For this reason the third annotation style is provided as well.

  1. someModule.factory(‘greeter’, [‘$window’, function(renamed$window) {
  2. // …
  3. }]);

Here, instead of simply providing the factory function, we pass an array, whose elements consist of a list of strings (the names of the depenendencies) followed by the function itself.

Keep in mind that all of the annotation styles are equivalent and can be used anywhere in Angular where injection is supported.

Where Can I Use DI?

DI is pervasive throughout Angular. You can use it when defining components or when providing run and config blocks for a module.

  • Components such as services, directives, filters and animations are defined by an injectable factory method or constructor function. These components can be injected with “service” components as dependencies.
  • The run and config methods accept a function, which can also be injected with “service” components as dependencies.
  • Controllers are defined by an constructor function, which can be injected with any of the “service” components as dependencies, but they can also be provided with special dependencies. See “DI in Controllers” below.

Factory Methods

Factory methods are responsible for creating most objects in Angular. Examples are directives, services, and filters. The factory methods are registered with the module, and the recommended way of declaring factories is:

  1. angular.module(‘myModule’, [])
  2. .factory(‘serviceId’, [‘depService’, function(depService) {
  3. }])
  4. .directive(‘directiveName’, [‘depService’, function(depService) {
  5. }])
  6. .filter(‘filterName’, [‘depService’, function(depService) {
  7. }]);

Module Methods

We can specify functions to run at configuration and run time for a module by calling the run and config methods. These functions are injectable with dependencies just like the factory functions above.

  1. angular.module(‘myModule’, [])
  2. .config([‘depProvider’, function(depProvider){
  3. }])
  4. .run([‘depService’, function(depService) {
  5. }]);

Controllers

Controllers are “classes” or “constructor functions” that are responsible for providing the application behavior that supports the declarative markup in the template. The recommended way of declaring Controllers is using the array notation:

  1. someModule.controller(‘MyController’, [‘$scope’, ‘dep1’, ‘dep2’, function($scope, dep1, dep2) {
  2. $scope.aMethod = function() {
  3. }
  4. }]);

This avoids the creation of global functions for controllers and also protects against minification.

Controllers are special in that, unlike services, there can be many instances of them in the application. For example, there would be one instance of a for every instance of the ng-controller directive in the template.

Moreover, additional dependencies are made available to Controllers:

  • $scope: Controllers are always associated with a point in the DOM and so are provided with access to the scope at that point. Other components, such as servies only have access to the singleton $rootScope service.
  • $route resolves: If a controller is instantiated as part of a route, then any values that are resolved as part of the route are made available for injection into the controller.

Setting-up AngularJS, Angular Seed, Node.js and Karma

I’ve used AngularJS for a few months, but I have no knowledge when it comes to testing AngularJS apps. I have a subscription to PluralSight.com and wanted to go through their online video training course for AngularJS. Specifically with this course I want to learn how to use Karma to do testing.

I’m usually extremely happy with PluralSight.com course, but in the beginning of this course I was somewhat disappointed. In Section 7 (“Angular Seed”) new technologies were introduced. The author introduced Angular-Seed, Node.js and Karma. I’ve worked with Node.js, but there are probably many people who have never used it. I believe the author took for granted that the student knew Node.js. For those who have never used Node.js this could be an obstacle.

When I started the course I didn’t have Node.js installed. I installed Node.js and things didn’t work as intended. I couldn’t run tests in Node.js because Karma was not installed. Once I installed Karma, Chrome wouldn’t launch in the tests.

With all the issues I was having, I wondered if others were having the same problems. If others were having similar issues, were they discouraged and not continuing with the course. So I decided to create this blog post to help others to get stared with AngularJS, Angular-Seed, Node.js and Karma.

Here are my assumptions:

  • You are a windows developer
  • Google Chrome browser is installed
  • You will use the Windows Command Prompt instead of Bash
  • You have little or no knowledge of Node.js
  • You have little or no knowledge of Karma
  • You will use Node.js as the web server. I will step you through this.
  • You have access to and IDE. Visual Studio will be fine. NotePad++ would also probably work. I use JetBrains WebStorm. JetBrains has a free 30 day trial for WebStorm.

Here are the high level step we will follow:

  1. Download and install Angular-Seed
  2. Download and install JetBrains WebStorm (Optional)
  3. Download and install Node.js
  4. Confirm Node.js is installed
  5. Run Karma Unit Tests – will fail because Karma is not installed
  6. Install Karma
  7. Run Karma Unit Tests again – Will fail because Chrome will not start
  8. Add System Variable to Windows
  9. Confirm System Variable Were Added
  10. Run Unit Tests again – should succeed
  11. Confirm that Units are being tracked by Karma
  12. Start Web Server by using Node.js

So lets get started.

1. Download and install Angular-Seed

Download Angular-Seed from GitHub at https://github.com/angular/angular-seed.

Extract Angular-Seed to a directory. I extracted it to “C:\Demo\Angular-Seed-Master”

2. Download and install JetBrains WebStorm (Optional)

Since PluralSight.com course for AngularJS is using the application JetBrains WebStorm, I will use it also. I will go through the steps of installing and setting up WebStorm; it’s very simple.

So install and run JetBrains WebStorm. They have a free 30 day trial. It can be downloaded from here: http://www.jetbrains.com/webstorm/download/. Any editor will be fine. I prefer WebStorm for editing HTML, JavaScript and CSS.

Once WebStorm is downloaded and installed, start the application. After you stared WebStorm you will eventually get to the “Quick Start” screen. On this screen select “Open Directory” (see image).

The “Select Path” dialog will be displayed. Navigate to the directory where you extracted Angular-Seed. Select “OK”. I extracted it to “C:\Demo\Angular-Seed-Master”

The WebStorm IDE should be displayed. On the left there should be a project structure with the “Angular-Seed-Master” folder. Click “+” to expand the directory structure.

3. Download and install Node.js

The test in Angular-Seed requires Karma-Runner. To run Karma you will need to install Node.js. I will provide instructions of how to install Node.js and Karma-Runner. After they are installed I will show how to run the tests.

Download and install Node.js.

Go to http://nodejs.org/ website and select the “install” button (see image below). This should download the version of Node.js you need. For me, when I clicked the “install” the version for Windows 64-bit downloaded. Once Node.js is download, execute the file.

I will not go thought the steps for installing Node.js; it’s very straight forward. During the install I used all default options.

4. Confirm Node.js is installed

When Node.js installation has completed we need to confirm it was installed successfully. Open a Windows Command Prompt and type “Node” and [enter]. You should get prompted with a “>”.

Now at the prompt type something like “2 + 2″ and [enter]. You should get a result. I got back 4. Now we have confirmed that Node.js is installed correctly. You can quit Node.js by using the keyboard and typing [ctrl]+C twice.

5. Run Karma Unit Tests

Now we are going run test to confirm that Node.js is ready to run Karma?

In WebStorm select the “test.bat” file; it’s in the scripts directory (see image below). There’s nothing you need to do in WebStorm at this time. I just want to point out that this is the file we will run in Node.js to execute Karma.

No go back to your Windows Command Prompt. If you are still in node, exit node by [ctrl]+C twice.

You need to navigate to the directory where you have un-compressed “Angular-Seed”. For me it’s “C:\Demos\angular-seed-master”.

Once at the Angular-Seed directory type “scripts\test.bat” in the command.

When you run test.bat you may get an error that “karma” is not recognized. If this occurs, “Karma” needs to be installed.

6. Install Karma

To install Karma, at the Windows Command Prompt type “npm install – g karma”. Insted use Update: “npm install – g karma@0.8.7″. !Ignore the image. Based on comments from dotnetsilverlightprism and confirmed, there seems to be breaking changes with new versions of Karma. If you install Karma version 0.8.7 everything seems to be working fine.

Now Karma should be installed.

7. Update angular-seed-master/config/karma.conf.js

Based on dotnetsilverlightprism comment (see his comment at the bottom of the post), after I originally published this post there was a breaking change with the newer Karma package and Angular Seed. This can be easily fixed by changing the file angular-seed-master/config/karma.conf.js with the following code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module.exports = function (config) {
    config.set({
        basePath: '../',
        frameworks: ['jasmine'],
        files: [
            'app/lib/angular/angular.js',
            'app/lib/angular/angular-*.js',
            'test/lib/angular/angular-mocks.js',
            'app/js/*.js',
            'test/unit/*.js'
        ] ,
        autoWatch: true,
        browsers: ['Chrome'],
        junitReporter: {
            outputFile: 'test_out/unit.xml',
            suite: 'unit'
        }
    })
}

8. Run Karma Unit Tests again

Now lets test the “test.bat” file again. Go to the Windows Command Prompt and confirm the location is still where you unzipped Angular-Seed. For me it’s “C:\Demos\angular-seed-master”.

In the Command Prompt enter “scripts\test.bat” and [enter].

You may receive another error in regards that Karma cannot start Chrome.

9. Add System Variable to Windows

To fix this, Windows System Environment Variables need to be updated to reference the Chrome Browser.

To changes the variables we need to open the Windows system properties. To do this follow these steps (see image below). (1)Click Windows “Start” Button. (2) Type “System Variables” in the Search Box. (3) Select the “Edit the system environment variables”.

This should display the following “System Properties” dialog. Select the “Environment Variables” button.

Now you should see the following “Environment Variables” dialog. In the “System Variables” section, click the “New” button.

In the “Edit System Variable” dialog, set the variable name to “CHROME_BIN”. Set Variable value to the path where Chrome is located. For me the path is “C:\Program Files (x86)\Google\Chrome\Application\chrome.exe”. The path for you may be different. And click “OK”. And click “OK” through the rest of the dialogs

10. Confirm System Variable Were Added

Important: If you still have Windows Command Prompt open, close it. The new system variable will not be available in the current Windows Command Prompt.

Important: Open a new Windows Command Prompt. This will give you a new context for system variables and the variable that we just added should be available now.

Let’s confirm that the system variable was added. In the new Window Command Prompt type “SET” and [enter]. In the results confirm that “CHROME_BIN” is there with the correct path. If the “CHROME_BIN” variable is not there, then one of the steps above may have been missed.

11. Run Unit Tests Again

In the Windows Command Prompt change the path to where you unzipped the Angular-Seed zip. For me it’s “C:\demos\angular-seed-master”

Now lets see if the test will run now. In the Windows Command Prompt type “scripts\test.bat”.

Now the tests should run and the Chrome browser should start. Now Karma is running in Node.js

12. Confirm that Units are being tracked by Karma

In the Window Command Prompt you should notice that all 5 unit test have completed successfully.

You should also notice that the test.bat script did not end. This is because Karma is watching your source files for changes. If you change one of your source files (JavaScript file), the unit test will be rerun.

Now return to your IDE and change a JavaScript source file and save the file. For me, my IDE is WebStorm and I changed the file “test\unit\controllerSpec.js” by adding a space. After changing the file I save it.

As soon as you change the file, Karma will rerun the tests.

13. Start Web Server by using Node.js

Lets confirm that Node.js can run your Angular-Seed website.

Now open a new Windows Command Prompt or stop the current test script ([ctrl]+C twice).

Confirm and navigate that your path for Windows Command Prompt is where you extracted your Angular-Seed zip. My location is “C:\demos\angular-seed-master”.

In the Windows Command Prompt enter “Node scripts\web-server.js”

In the Windows Command Prompt, you should be notified that an HTTP server is running.

Now open a browser and navigate to “http://localhost:8000&#8243;.

You should see something similar to the following image.

That’s it. Hopefully everything is work and now you can create AngularJS applications using Karma, Angular-Seed, and Node.js