AngularJS blog series – MVC in AngularJS

At AppNexus, we have been using the JavaScript framework AngularJS. We like it, and we’d like to share what it is and how to use it.

This is one in a series of posts about AngularJS. Here is the general introduction to AngularJS and the series list.

To illustrate AngularJS we will build a (very) simple user subscription feature similar to one we implemented for the AppNexus Twixt app. The UI will display a list of candidates in a dropdown and the user can select one or more candidates to be subscribers. Subscribers are then added to the subscriber list table, and removed from the candidate list. Unsubscribing a subscriber adds them back into the candidate list.

It looks like this:

mock.png

The Model, View, and Controller for this feature are built as follows:

  • model – a list of candidates with two key properties: name (string) and isSubscriber (boolean)
  • view – simple HTML layout of the form
  • controller – called SubscriptionController

Next we will walkthrough creating the view, controller, simple view model and supporting service.

For those who can’t wait, click here to see the implementation at the top of the page. Below it is a split screen display of the javascript code and accompanying comments. You’ll have to inspect to see the HTML.

Create the View (HTML)

The HTML for the dropdown of participants is as follows:

       <select ng-model=”model.selected”

         ng-options=”participant.name for participant in model.participants | filter:{isSubscriber: false} | orderBy:’name’”>

       </select>

You can see this is an HTML select box. But what’s with the ng-model and ng-options?

Well, Angular redefined the HTML <select> and basically superpowered it. This is called a Directive in Angular, and is a very powerful feature of Angular because you can (and will) create your own custom Directives too.

In the built-in select Directive you can:

  • Specify a model property in ng-model to hold the current selection
  • Use an iterator to build the list of options
  • Add an Angular filter, to adjust the iterator output. In our case the filter only shows participants who are not subscribers (isSubscriber === false) and sorts by name

Is your mind blown yet?

I’d note that directives are similar to the W3C Web Components draft standard, and the AngularJS team has plans to support this standard. Twixt makes extensive use of built-in and custom directives and directives will be addressed further in a forthcoming blog post.

Now to display the list of subscribers, iterate over the same model list (model.participants) but this time show only participants who are subscribers (isSubscriber === true). Here’s the key HTML:

      <tr ng-repeat=”subscriber in subscribers = (model.participants | filter:{isSubscriber: true})”>

              <td>{{subscriber.name}}</td>

      </tr>

Notice the ng-repeat?

AngularJS defines a general-purpose iterator, ng-repeat, which is used to iterate over the list of participants. This subscriber list is also filtered for participants who are subscribers (the opposite of the previous list).

The {{subscriber.name}} is an example of an AngularJS data binding and will result in the subscribers name being displayed. Data binding syncs the UI to the model. If the name is changed in the model then the UI would update “automagically” to show it. That’s AngularJS data binding.

The reader may notice only one model (model.participants) is used by both lists. By toggling a property isSubscriber for each participant, it will appear/disappear in the appropriate list.

Angular is smart enough to detect model changes and automatically keep the lists updated (as subscribers are  added/removed). More may be said on this topic, but that is a blog post topic in itself.

The HTML view includes instructions which tells Angular to use the SubscriptionController from the subscriptions app for this part of the page:

<div ng-app=”subscriptions”>
<div ng-controller=”SubscriptionController” class=”form”>

This is a way, but not the only way to wire up controllers and views.

 

Write Controller and Service

Define a Module

Angular has a module system to enable you to define your own modules, build your app from modules, declare module dependencies and include modules others have written.  “You can think of a module as a container for the different parts of your app – controllers, services, filters, directives, etc.”

Here we define an AngularJS module named ‘subscriptions’ to contain our subscriptions feature code as follows:


1
 angular.module('subscriptions', [])

Note the empty array list [] at the end. One can list other modules as dependencies there.

Define a Controller

AngularJS supports defining controllers (the C in MVC). Angular controllers work like typical MVC controllers: make data available to the view and respond to events. One significant difference between Angular controllers and many other JavaScript framework/library controllers is that you don’t manipulate the browsers Document Object Model (DOM) in an Angular controller. Instead, data binding or directives are used to modify the DOM. A major reason for this is to make AngularJS code much more testable by decoupling the controller code from the dom.

Next we define a SubscriptionController within the subscriptions module to handle loading the data and responding to UI events such as unsubscribe/subscribe and save changes.

 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
angular.module('subscriptions', [])
.controller('SubscriptionController', ['$scope', 'SubscriptionService',
    function($scope, SubscriptionService) {
    // $scope.model is an object that will be a holder for data to/from the view;
    // $scope is an object automatically provided by angular per controller instance
    $scope.model = {};

    // call the service to load the data, when loaded then provide the
    //  participant list to the view via $scope.model (an angular pattern)
    SubscriptionService.getParticipants()
        .then(function(participants) {
            $scope.model.participants = participants;
        });

    // listen for user selecting a participant to be a subscriber;
    // $watch sets up a listener callback whenever a watchExpression changes
    $scope.$watch("model.selected", function(newValue, oldValue) {
        if (newValue === oldValue || newValue == null) return;

        // make the selected participant a subscriber
        $scope.model.selected.isSubscriber = true;
    });

    // user unsubscribed a subscriber, update the subscriber to no longer
    // be a subscriber
    $scope.removeSubscriber = function(subscriber) {
        subscriber.isSubscriber = false;

        $scope.model.selected = null; // reset choice
    };

    // user choose to save changes so call service to save subscriber list
    $scope.saveChanges = function() {
        SubscriptionService.updateParticipants($scope.model.participants)
            .then(function() {
                // handle save success
            },
            function() {
                // handle save failure
            });
    }

}])

Note the SubscriptionController declares a dependency on a SubscriptionService which will be defined next. Because of this declared dependency, Angular will make the SubscriptionService available to the controller. This is Angulars Dependency Injection in action and is a powerful enabler for unit testing because it enables substitution of mock objects for real objects while unit testing (thus allowing testing in isolation).

 

Define a Service

AngularJS provides a built in abstraction layer called Services, to allow you “organize and share code across the app”. A service can be whatever you want it to be, from an API layer to a cache; AngularJS itself provides a number of useful pre-built services that are used in Twixt.

Define a SubscriptionService for use by the Controller (a common use case)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.service('SubscriptionService', ['$q', function($q) {
    // return a list of participants from api
    this.getParticipants = function() {

        // resolve the promise immediately with dummy data; but
        // typically would call to ajax api to get list of participants
        var defer = $q.defer();
        defer.resolve([{name: "Jane", id: 1, isSubscriber: false },
                    {name: "John", id: 2, isSubscriber: false },
                    {name: "Anne", id: 3, isSubscriber: false },
                    {name: "Bob", id: 4, isSubscriber: false  },
                    {name: "Luther", id: 5, isSubscriber: false  },
                    {name: "Clarissa", id: 6, isSubscriber: false  }
                    ]);

        return defer.promise;
    };

    // add/remove subscribers by call to api
    this.updateParticipants =function(participants) {
        // no implementation here, just return a resolved promise
        var defer = $q.defer();
        defer.resolve();
        return defer.promise;
    };

}])

Here’s the implementation code and working feature. Below it is a split screen display of the javascript code and accompanying comments. You’ll have to inspect to see the HTML.

Unit Testing

No introduction to AngularJS would be complete without highlighting Angulars support for testing. The Angular team strongly believes in robust testing and the framework is engineered to support it through patterns such as Dependency Injection, rules about DOM manipulation, mock services and APs such as expects which return dummy data for HTTP requests when testing.

For unit testing the AngularJS team created Karma, a test runner. Its easy to setup and you can write tests using the describe syntax with Jasmin/Mocha etc. Twixt has over 1300 unit tests and counting.

Here’s an outline of what a controller unit test could be for SubscriptionController (but it is contrived and is not complete)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    describe('SubscriptionController', function() {
        var scope = null;

        beforeEach(inject(function($httpBackend, $rootScope, $controller) {
            scope = $rootScope.$new();

            // instantiate the controller (which will trigger a load of the participants)
            $controller('SubscriptionController', {$scope: scope});
        }));

        it('should be initialized with participants', function() {
            // expect we'll have at least one
            expect(scope.model.participants.length).toBeGreaterThan(0);
        });

        it('should be able to add subscriber', function() {
            // confirm first participant is not a subscriber
            expect(scope.model.participants[0].isSubscriber).toBeFalsy();

            // set selected to first participant and thus make them a subscriber
            scope.model.selected = scope.model.participants[0];

            // trigger the angular watch
            scope.$digest();

            // confirm first participant is indeed now a subscriber
            expect(scope.model.participants[0].isSubscriber).toBeTruthy();
        });        
    });

For e2e functional testing the Angular team uses Protractor. Protractor uses WebDriver on top of Selenium. One nice feature is one can use the same syntax to write e2e tests as for unit tests. Twixt has over 1500 e2e tests and all run on Chrome, FF, IE10 and 11 (a subset run on Safari and IE9). These can be run asynchronously.

e2e testing is basically simulating an end user, logging into the app, navigating to pages, entering data etc. Its pretty awesome to see it when it runs and something we’d like to demo in the future.

Well, thats it. Our whirlwind tour covered Model View Controller in angularjs as well as briefly touching on Directives, modules, testing and Dependency Injection. Let us know your thoughts and feedback on AngularJs in the comments.

 

comments (0)

AngularJS blog series – Introduction

Greetings from your AppNexus Discovery Engineering team in San Francisco! In the spirit of AppNexians sharing, the team here is going to write a series of blog posts about technologies we’ve used to create Twixt, a brand-new application for direct media buying. (If you haven’t heard about Twixt, check out twixt.it)

twixt_logo.pngTwixt is a single page web app, or SPA, built using a number of client and server technologies. The application client that runs in a users browser relies mainly on the JavaScript framework AngularJS. Through these blog posts, we are going to help you better understand what AngularJS (“Angular” for short) is and how we use it.

A single page app, or SPA, is a web app that loads itself mostly all at once at a single URL. Well-organized JavaScript then runs as a single application within the page, making AJAX calls to the server to load information and perform tasks, but never changing the base URL (although the path after the # may change – more on this later). HTML fragments may be loaded from the server to provide layouts for the app, but these are assembled (along with data) into views and injected into the Document Object Model (DOM) by the application itself – everything is handled on the fly by JavaScript.

Single page web apps are popular these days as they allow for the smoothest “desktop app in a browser” experience. There is less flicker between pages and “persistent UI” is truly persistent. If you’ve used Gmail, Google Maps, or listened to SoundCloud, then you’ve used a single page app, and many other big industry players treat large sections of their sites as SPAs.

Technologies

Let’s take a step back for a minute. The arrival of AJAX, jQuery, and vastly improved JavaScript engines such as SpiderMonkey and V8 made creating rich user experiences in the browser possible, and then easier than ever. Developers capitalized on the ubiquitous power of modern computers to push more and more heavy lifting to the client (browser) side.

Not surprisingly, code complexity on the client also rapidly increased. It’s not uncommon to have thousands of lines of JavaScript and jQuery on just a few HTML pages. This code can often be hard to understand and maintain, disorganized and difficult to test.

term3_5_copy0.jpg

This didn’t happen yet

Then came the rise of the machines, (ahem) JavaScript frameworks/libraries, such as Backbone, JavaScriptMVC, SproutCore, Ember and AngularJS. These frameworks all support time-tested patterns such as Model View Controller (MVC), which brings logical order to such UI code complexity. The MVC pattern has been around since the 1970s and implemented in a variety of languages such as Smalltalk, java Spring, symfony php, .Net, PureMVC actionscript and more.

Recently the MVC pattern has gained wide adoption in web development. In addition, these JS frameworks do routing, state management and other concepts relevant to navigating around an application (things we used to think of as “browsing” with traditional websites). The layers of complexity that were tamed by these frameworks also led to a new frontier of SPAs.

logo300.png

Right, Angular

And so now to AngularJS: Developers use the AngularJS framework to build single page web apps. AngularJS is a comprehensive framework: it has opinions on how it expects web apps to be written and what best practices should be followed; it provides a large set of framework features such as data binding, a module system, dependency injection, directives, and support for MVC; it supports important long-term sustainability features such as unit testing, end to end (e2e) testing and more.

In this blog series, we’ll take a deep dive into selected features of AngularJS technology with callouts about how we used it in the Twixt app. We plan to write blog posts about:

  • MVC in AngularJS
  • Directives
  • Routing
  • Useful patterns
  • And more

We’re open to suggestions! If there’s a topic you’d like us to cover, something you’d like to understand better, or questions we can answer, please let us know. We will try to accommodate and enlighten.

This is the first post in the series MVC in AngularJS.

There are some great resources for learning more about AngularJS including:

comments (1)

K-ary heapsort: more comparisons, less memory traffic

The impetus for this post was a max heap routine I had to write because libc, unlike the STL, does not support incremental or even partial sorting. After staring at the standard implicit binary heap for a while, I realised how to generalise it to arbitrary arity. The routine will be used for medium size elements (a couple dozen bytes) and with a trivial comparison function; in that situation, it makes sense to implement a high arity heap and perform fewer swaps in return for additional comparisons. In fact, the trade-off is interesting enough that a heapsort based on this routine is competitive with BSD and glibc sorts. This post will present the k-ary heap code and explore the impact of memory traffic on the performance of a few classical sort routines. The worst performing sorts in BSD’s and GNU’s libc overlook swaps and focus on minimising comparisons. I argue this is rarely the correct choice, although our hands are partly tied by POSIX.

Continue reading

comments (4)

Enable Your Python Developers by Making “Code Investments”

Enable Your Python Developers by Making “Code Investments”

Note: portions of this post appeared on my personal blog under the title “Supercharge Your Python Developers

I think it’s safe to say that a project’s inception is the best, indeed perhaps only, opportunity to influence the quality of the code for years from now. Many (most?) projects are started without much direction; code simply springs into being and is put under version control. Making a series of thoughtful, upfront “investments,” however, can pay large dividends. In this post, I’ll describe investments I made at the start of a project that allowed a Python novice to quickly write concise, idiomatic, and well-tested code.

Continue reading

comments (0)

Hash Set versus Dense Hash

During the development of the Concurrency Kit hash set and hash table, detailed microbenchmarks were used to measure latency and variability of various operations in relation to various open-source hash table implementations. For read-mostly workloads, the implementation was at least twice as fast than Google Dense Hash on reference machines even though it provides stronger forward-progress guarantees for concurrent workloads. For example, it is lock-free for readers and wait-free for writers in single-writer many-reader use-cases. However, a recent use-case required the hash table implementations to handle delete-heavy workloads. As many open-addressing schemes, the implementation failed miserably in this workload due to tombstone accumulation. The strength of the collision resolution mechanism would very quickly lead to the complete elimination of probe sequence termination markers (empty slots) in favor of tombstones. Google Dense Hash performed in a stable manner with the delete-heavy workloads at the cost of a higher operation completion floor for more favorable workloads (due to increased clumping of the quadratic probing it uses). The performance gap for delete-heavy workloads has since then been closed with some trade-offs.

Continue reading

comments (1)