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:
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.
Create the View (HTML)
The HTML for the dropdown of participants is as follows:
1 2 3 4
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:
1 2 3
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 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:
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:
Note the empty array list  at the end. One can list other modules as dependencies there.
Define a Controller
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
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
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
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.