Many JavaScript libraries have hit the scene in the past decade. Most of them share the same intent: Abstract the JavaScript language to make it more user-friendly. The JavaScript library movement really gained traction with Prototype, Dojo, Mootools, and of course, jQuery. With the exception of Dojo, JavaScript libraries lack the declarative simplicity of HTML and Cascading Style Sheets (CSS). Simply put, you have to think like a programmer to use them. AngularJS promises to change that, so that web designers can add functionality to their pages without writing a bunch of code. Moreover, AngularJS allows experienced coders to develop web applications with less code, thus saving time. In this article, I will outline the steps required to display some dynamic content in a web page using AngularJS.

Declarative vs. Imperative Programming

In the previous paragraph, I spoke about "the declarative simplicity of HTML and CSS." To help you better understand why that's a good thing, let's review what makes a language declarative as opposed to imperative.

Declarative programming is when you write your code in such a way that it describes what you want to do, not how you want to do it. As such, it is left up to the compiler or interpreter to figure out the how. SQL and XSTL are both examples of declarative programming languages.

Imperative programming is telling the machine how to do something, with the end result being what you want to happen. As for examples, just about every programming language, including Visual Basic, Java, and C++, is imperative.

Typically, a language falls squarely into one of the two camps. JavaScript, the language of AngularJS, is a rare breed -- flexible enough to be used either way, so it's often used to present side-by-side comparisons. That being said, most of the examples that I've come across compare declarative programming to its functional equivalent. Functional programming is a form of declarative programming that implements a function-based approach to problem solving. Perhaps the most noteworthy ramification of the black-box approach of functional languages is that they are stateless; that is, the value returned by a function is wholly dependent on its input. Functions in imperative languages allow for the creation and transformation of data so that subsequent function calls may modify a variable.

Instead, let's look an example using Dojo, since it's one of the few JavaScript-based frameworks that supports both declarative and imperative styles. Using the declarative style, one only needs to include the data-dojo-type attribute to the element tag to create a Dojo widget. When the page is loaded, Dojo parses the page markup for these identifiers and dynamically appends code to convert the element into the widget type set in the data-dojo-type's value:

<button type="button" id="myButton" data-dojo-type="dijit/form/Button">
    <span>Click Me!</span>
</button>

If we so choose, we can imperatively invoke the parser ourselves by adding an additional JavaScript block to the page:

<script type="text/javascript">
    require(["dojo/parser", "dijit/form/Button", "dojo/domReady!"],
    function(parser){
        parser.parse();
    });
</script>

We can glean from these two snippets that, although imperative programming is more work for the developer, it also affords us additional freedom and control over processes.

Coding in AngularJS Style

AngularJS espouses the use of declarative programming for building the user interface (UI), while it employs traditional imperative programming to wire the logic behind the UI. AngularJS bears a strong resemblance to Dojo, but AngularJS extends traditional HTML markup to establish two-way data binding that allows for the automatic synchronization of UI components and the underlying business model(s).

A basic example. Synchronizing the front end with the underlying business logic instantly renders a page more dynamic, because changes to either are immediately reflected in the other. For instance, Listing 1 and Listing 2 show two versions of the "Hi, how are you?" page that allows the user to state how he or she is feeling.

Listing 1: 'Hi, how are you?' Page Written in jQuery
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">

  <title>How Are You?</title>
  <script type="text/javascript" src="scripts/jquery-1.6.4.js"></script>
  <script type="text/javascript" charset="utf-8">
      $(document).ready(function() {
          var $txtMood = $('#txtMood'),
              $spnMood = $('#spnMood');
         
          $txtMood.keyup(function() {
                  $spnMood.text($txtMood.val());
          });
      });
  </script>
</head>
<body>
    <h1>How are you feeling right now?</h1>

    <div>
      <input type="text" id="txtMood" />
      <h2>I am feeling <span id="spnMood"></span>.</h2>
    </div>
  </div>
</body>
</html>

Based on Daniel Johnston's ground-breaking demo tape of the same name, Listing 1 is written in an imperative style using jQuery-empowered JavaScript; the code in Listing 2 achieves the same functionality using AngularJS's declarative approach. The code that handles the dynamic updates is highlighted in red.

Listing 2: 'Hi, how are you?' Page Written in AngularJS
<!DOCTYPE html>
<html lang="en" ng-app>
<head>
  <meta charset="utf-8">

  <title>How Are You?</title>
  <script type="text/javascript" src="scripts/angular.js"></script>
</head>
<body>
    <h1>How are you feeling right now?</h1>

    <div>
      <input type="text" ng-model="mood" />
      <h2>I am feeling {{mood}}.</h2>
    </div>
  </div>
</body>
</html>

In Listing 1, characters typed into the txtMood text field are reflected in between the spnMood <span> tags in real time. There are a couple of steps to making this work. First, we need to place code in the document's ready() handler so that we can reference the two controls once the page has finished loading. Thanks to the jQuery $ selector, we can easily locate the controls using their IDs. Next, a listener must be bound to the txtMood's keyup() event. In the event handler function, the spnMood's text is updated to that of the txtMood's value.

Compare the code used in Listing 1 with the amount of code required using AngularJS, as shown in Listing 2. We still need a reference to the external AngularJS library, but there is no JavaScript code to be found in the page markup. Although jQuery also allows for the complete separation of markup and JavaScript code, it still requires us to write some imperative code, including conditional logic, event handlers, loops, object references, expressions, and so on. Using AngularJS's purely declarative style, we merely need to:

  1. Include the ng-app attribute in the opening <HTML> tag.
  2. Reference the AngularJS library.
  3. Designate the model using the ng-model attribute.
  4. Display the model value via the double curly braces -- {{}}. That's AngularJS's expression delimiter.

The next several sections give more detail about each of these four elements, so that you can create more elaborate web applications.

Bootstrapping Your Application

Including the ng-app directive kicks off automatic initialization as soon as the Document Object Model (DOM) has finished loading. The node that contains the directive is designated as the application root. Placing it in an inner tag tells AngularJS to treat only that portion of the DOM as an AngularJS application.

Automatic initialization works for most applications, but for those rare instances where you require more control, you can revert to manual -- read imperative -- bootstrapping instead. That involves replacing the ng-app directive with a second script containing an event handler for document.ready(), as shown in Listing 3. Inside the handler, there are two necessary lines: The first defines your application as a module, and the second calls the bootstrap() method with the root node and an array of module names.

Listing 3: Bootstrapping an Application
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">

  <title>How Are You?</title>
  <script type="text/javascript" src="scripts/angular.js"></script>
  <script type="text/javascript">
    angular.element(document).ready(function() {
        angular.module('HowAreYouApp', []);
        angular.bootstrap(document, ['HowAreYouApp']);
    });
</script>
</head>
<body>
    <h1>How are you feeling right now?</h1>

    <div>
      <input type="text" ng-model="mood" />
      <h2>I am feeling {{mood}}.</h2>
    </div>
  </div>
</body>
</html>

Referencing the AngularJS Library

For running your AngularJS app in production, you might consider changing your .html file or script loader to use Google Content Delivery Network (CDN) URLs rather than host the AngularJS files yourself.

The URL for the main angular.min.js file is https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js. There are additional modules as well as non-minified versions meant for development and debugging purposes. Here's the full list:

angular-sanitize.js
angular-resource.min.js
angular-resource.js
angular-mocks.js
angular-cookies.js
angular.min.js
angular-loader.js
angular.js
angular-loader.min.js
angular-cookies.min.js
angular-sanitize.min.js

Supported Directives

Attributes such as ng-app and ng-model that we added to HTML tags are markers called directives and tell AngularJS to attach a specified behavior to that DOM element. AngularJS comes with several other predefined directives, including these:

  • ng-app: Declares the root element of the application.
  • ng-bind: Dynamically changes the text of an HTML element to the value of a given expression.
  • ng-model: Similar to ng-bind, but allows two-way data binding between the front end and the script.
  • ng-class: Dynamically loads class attributes.
  • ng-repeat: Creates an element for each item of a collection.
  • ng-show/ng-hide: Conditionally shows or hides an element, depending on the value of an expression that evaluates to a Boolean value.
  • ng-switch: Conditionally instantiates one template from a set of choices, depending on the value of a selection expression.
  • ng-if: Shows the following element if the given conditions are true.
  • ng-controller: Specifies a JavaScript controller class that evaluates HTML expressions.
  • ng-view: Includes a template in the main layout (index.html) file.

AngularJS Expressions

Expressions are JavaScript-like code snippets between double curly braces -- {{ expression }} -- that are processed by the AngularJS parser. Although AngularJS expressions bear some resemblance to the <%=expression> output shortcuts of ASP and JavaServer Pages (JSP), they behave more like the JavaScript eval() function, in that expressions are evaluated on the client side.

When you use AngularJS expressions, such as the examples in Listing 4, keep in mind that they are evaluated within the current scope, which extends only to the application root node. In contrast, JavaScript expressions are evaluated against the global window. Moreover, they may contain only one-line expressions, so control flow statements such as conditionals, loops, or throws are not allowed. On the plus side, AngularJS is pretty lenient, so null and undefined variables won't throw an error as they would in regular JavaScript.

Listing 4: AngularJS Expressions
<body>
  <h1>Today's Math Lesson</h1>
  <p>Hello {{user.name}}.</p>
  <pre>
   1+1={{1+1}}
  </pre>
</body>

Manipulating Expressions Using Filters

One of the most common operations on data is to format it to suit the context. For instance, currency, dates, and numbers are all formatted differently, depending on the audience and/or locale. These and other filters are already predefined for you. To use a filter, simply append it to the expression, as follows:

{{ expression | filter }}

Filters can be chained together:

{{ expression | filter1 | filter2 | ... }}

You can also pass arguments to them:

{{ expression | filter:argument1:argument2:... }}

For example, appending the currency filter to the number 100

{{ 100 | currency }}

formats the number as $100.00.

The previous example uses the default locale to decide which currency to use. You can override this behavior to use a specific currency by supplying the optional argument. The only question is, how do you type a euro or yen symbol if you aren't familiar with these currency types? The answer is to use HTML constants enclosed in quotes. That treats the contents as an HTML string, as the following example shows:

<p>Your total comes to {{1000 | currency:"&euro;"}}
   OR {{138546.56 | currency:"&yen;"}}
   OR {{1358.70 | currency:USD}} ({{1358.70 | currency}})</p>

This code produces the following output:

Your total comes to €1,000.00  OR  ¥138,546.56  OR  $1,358.70 ($1,358.70)

Note that the US dollar value is not enclosed in quotes. If it were, a value of USD1,358.70 would have been displayed instead.

Here are the most commonly used filters:

currency
number
date
filter
json
limitTo
orderBy
lowercase
uppercase

Encapsulating Business Logic Inside of a Service

Modern-day JavaScript applications have become a lot more complex than they used to be. As the amount of JavaScript code required to run your application increases, so does the importance of your application architecture. As part of a sound design, your application should use services wherever possible. There are several good reasons for using services, but probably the most important reason is that they decouple what a component does from how as task is carried out and what carries out the task.

There are a couple of principles of object-oriented design that pertain directly to services. The single-responsibility principle (SRP) asserts that a class should have only one responsibility. The dependency-inversion principle (DIP) states that one should rely on abstractions rather than concrete implementations of a provider. For instance, a print service would expose a public method called print(). Behind the scenes, the concrete implementation of the printing could change, but the interface would not.

Following these guidelines, one could define a service as a stateless process that performs a single specific task (a few closely related tasks would also be acceptable). Hence, the public interface of a service always comprises functions as opposed to properties, because properties would suggest some sort of state.

There are two widely used ways to define an AngularJS service: by using module.factory or module.service. The difference between the two modules can be confusing to the uninitiated because their signatures are similar:

module.service( 'serviceName', function );
module.factory( 'factoryName', function );

The primary distinguishing feature is that serviceName is a constructor:

new serviceName ();

whereas factoryName is a function:

factoryName ();

The ramifications are that a service is better suited for sharing data across the application because it returns the entire object as opposed to just the return value.

Let's look at how to construct a service that performs a stock lookup. The first step is to create an application module via the angular.module() method. The module accepts two parameters: the module name and an array of other module names on which our module depends.

var app = angular.module('myApp', []);

We can now define our service by invoking the service() method on our application module. This method also takes two parameters. The first is the name of our service; the second is a function that encapsulates all the service's methods. Our service will implement two methods called getStocks() and getQuoteForSymbol(). The first method retrieves all the stocks, and the second fetches information for a specific stock.

We're going to store the stocks in an array of objects, which is AngularJS's preferred collection structure. In a real application, the stock service would likely use a web service or perform some sort of Ajax call to fetch the information.

The getQuoteForSymbol() method makes use of AngularJS's built-in "filter" $filter. (Yes, it's a filter called filter!) It's part of the same collection as the currency filter that we saw earlier. These can be invoked directly from JavaScript using the $filter('filter-type')(array, expression, comparator) syntax. Listing 5 shows the complete service-creation code.

Listing 5: An AngularJS Service
<script type="text/javascript" charset="utf-8">
    var app = angular.module('myApp', []);
     
    // Service definition
    app.service('stockService', function(){
        var stocks = [
                  { "symbol": "ABC",
                    "price": 87.86,
                    "change": -0.41
                  },
                  { "symbol": "DEF",
                    "price": 62.79,
                    "change": 0.49
                  },
                  { "symbol": "GHI",
                    "price": 67.64,
                    "change": 0.05
                  }];  
         this.getQuoteForSymbol = function(stocks, $filter, smbl){
             var found = $filter('filter')(stocks, {symbol: smbl}, true);
                   return found[0];
         };      
         this.getStocks = function() {
             return stocks;
         }  
    });

In the model-view-controller (MVC) design pattern, the controller acts as the intermediary between the UI (the view) and the underlying business logic. The controller presents relevant views based on the presented data (the model). Going the other way, the controller receives user input from the view, maps it into the appropriate function call(s), and invokes the function(s) on the responsible business component(s).

AngularJS controllers are created as either services or factories. The controller() constructor is invoked on your application object with a name argument and a function that encapsulates your controller logic.

Notice in the example shown in Listing 6 that we are injecting the $scope and $filter services into our controller, along with our own stockService, via function parameters. Much like the Spring Framework for enterprise Java applications, AngularJS comes with a dependency injection container built in to it. Handling dependency injection is something that AngularJS does very well. As long as AngularJS recognizes the argument names, it will pass the correct objects to our controller. That's why the first argument of constructor functions is a name.

Listing 6: Dependency Injection in AngularJS
// AngularJS Controller that uses the service
app.controller('StockController', function($scope, $filter, stockService) {    
    $scope.stocks = stockService.getStocks();
               
    $scope.getQuote = function() {    
        $scope.quote =
            stockService.getQuoteForSymbol($scope.stocks, $filter, $scope.selection.symbol);
    }    
});

As controllers tend to do, our StockController shuttles data between the stockService and the web page. In AngularJS, this is done via the $scope object.

In the HTML markup, we now have to set the ng-app directive to our application to create an application scope. There is also a reference to the StockController using the ng-controller directive. A listbox is populated with the available stock symbols using the declarative style. A button click invokes the getQuote() function (the select's onchange event could have worked as well). Stock details then show up in a <div>, as in Listing 7.

Listing 7: Setting the ng-app Directive in the HTML Markup
<html lang="en" ng-app="myApp">
...
    <div ng-controller="StockController">
      <select ng-model="selection" ng-options="s.symbol for s in stocks"
              ng-init="selection = stocks[0]" >
      </select>
      <button ng-click="getQuote()">Get Quote</button>                
        <div ng-show="quote">
          Quote for {{quote.symbol}}:<br>
          Price: {{quote.price}} <br>
        Change: {{quote.change}}
        </div>
     </div>

Figure 1 shows the resulting output in the browser.

Figure 1: Output of AngularJS Stock-Lookup Code

Worth the Effort

AngularJS's greatest strengths lie in its use of MVC, data binding, dependency injection, and declarative front-end style. Although you'll need to invest some time in learning to use AngularJS, it's an endeavor that will likely lead to a lot of time savings down the road. And in case you're wondering, AngularJS does get along very well with other JavaScript libraries, so you can use it in conjunction with something like jQuery to ramp up your development efforts even further!