AngularJS

AngularJS Essentials


August 2014

$10.00

Design and construct reusable, maintainable, and modular web applications with AngularJS.

In this article, by Rodrigo Branas, author of the book, AngularJS Essentials, we will go through the basics of AngularJS. Created by Miško Hevery and Adam Abrons in 2009, AngularJS is an open source, client-side JavaScript framework that promotes a high productivity web development experience.

It was built over the belief that declarative programming is the best choice to construct the user's interface, while imperative programming is much better and preferred to implement the application's business logic.

To achieve that, AngularJS empowers the traditional HTML by extending its current vocabulary, making the life of developers easier.

The result is the development of expressive, reusable, and maintainable application components, leaving behind a lot of unnecessary code and keeping the team focused on the valuable and important things.

(For more resources related to this topic, see here.)

Architectural concepts

It's been a long time since the famous Model-View-Controller, also known as MVC, started to be widely used in the software development industry, thereby becoming one of the legends of the enterprise architecture design.

Basically, the model represents the knowledge that the view is responsible to present, while the controller mediates their relationship. However, these concepts are a little bit abstract, and this pattern may have different implementations depending on the language, platform, and purposes of the application.

After a lot of discussions about which architectural pattern the framework follows, its authors declared that from now on, AngularJS is adopting Model-View-Whatever (MVW). Regardless of the name, the most important benefit is that the framework provides a clear separation of the concerns between the application layers, providing modularity, flexibility, and testability.

In terms of concepts, a typical AngularJS application consists primarily of view, model, and controller, but there are other important components, such as services, directives, and filters.

The view, also called template, is entirely written in HTML, which becomes a great opportunity to see web designers and JavaScript developers working side-by-side. It also takes advantage of the directives mechanism, a kind of extension of the HTML vocabulary that brings the ability to perform the programming language tasks, such as iterating over an array or even evaluating an expression conditionally.

Behind the view, there is the controller. At first, the controller contains all business logic implementation used by the view.

However, as the application grows, it becomes really important to perform some refactoring activities, such as moving the code from the controller to other components like services, in order to keep the cohesion high.

The connection between the view and the controller is done by a shared object called scope. It is located between them and is used to exchange information related to the model.

The model is a simple Plain-Old-JavaScript-Object (POJO). It looks very clear and easy to understand, bringing simplicity to the development by not requiring any special syntax to be created.

Setting up the framework

The configuration process is very simple and in order to set up the framework, we start by importing the angular.js script to our HTML file. After that, we need to create the application module by calling the module function, from the Angular's API, with it's name and dependencies.

With the module already created, we just need to place the ng-app attribute with the module's name inside the html element or any other that surrounds the application. This attribute is important because it supports the initialization process of the framework.

In the following code, there is an introductory application about a parking lot. At first, we are able to add and also list the parked cars, storing it’s plate in memory. Throughout the book, we will evolve this parking control application by incorporating each newly studied concept.

index.html

<!doctype html>
<!-- Declaring the ng-app -->
<html ng-app="parking">
  <head>
    <title>Parking</title>
    <!-- Importing the angular.js script -->
    <script src="angular.js"></script>
    <script>
      // Creating the module called parking
      var parking = angular.module("parking", []);
      // Registering the parkingCtrl to the parking module
      parking.controller("parkingCtrl", function ($scope) {
        // Binding the car’s array to the scope
        $scope.cars = [
          {plate: '6MBV006'}, 
          {plate: '5BBM299'}, 
          {plate: '5AOJ230'}
        ];
        // Binding the park function to the scope
        $scope.park = function (car) {
          $scope.cars.push(angular.copy(car));
          delete $scope.car;
        };
      });
    </script>
  </head>
  <!-- Attaching the view to the parkingCtrl -->
  <body ng-controller="parkingCtrl">
    <h3>[Packt] Parking</h3>
    <table>
      <thead>
        <tr>
          <th>Plate</th>
        </tr>
      </thead>
      <tbody>
        <!-- Iterating over the cars -->
        <tr ng-repeat="car in cars">
          <!-- Showing the car’s plate -->
          <td>{{car.plate}}</td>
        </tr>
      </tbody>
    </table>
    <!-- Binding the car object, with plate, to the scope -->
    <input type="text" ng-model="car.plate"/>
    <!-- Binding the park function to the click event -->
    <button ng-click="park(car)">Park</button>
  </body>
</html>

The ngController, was used to bind the parkingCtrl to the view while the ngRepeat iterated over the car's array. Also, we employed expressions like {{car.plate}} to display the plate of the car. Finally, to add new cars, we applied the ngModel, which creates a new object called car with the plate property, passing it as a parameter of the park function, called through the ngClick directive.

To improve the loading page performance, it is recommended to use the minified and obfuscated version of the script that can be identified by angular.min.js. Both minified and regular distributions of the framework can be found on the official site of AngularJS, that is, http://www.angularjs.org, or they can be directly referenced to Google Content Delivery Network (CDN).

What is a directive?

A directive is an extension of the HTML vocabulary that allows the creation of new behaviors. This technology lets the developers create reusable components that can be used within the whole application and even provide their own custom components.

It may be applied as an attribute, element, class, and even as a comment, by using the camelCase syntax. However, because HTML is case-insensitive, we need to use a lowercase form.

For the ngModel directive, we can use ng-model, ng:model, ng_model, data-ng-model, and x-ng-model in the HTML markup.

Using AngularJS built-in directives

By default, the framework brings a basic set of directives, such as iterate over an array, execute a custom behavior when an element is clicked, or even show a given element based on a conditional expression and many others.

ngBind

This directive is generally applied to a span element and replaces the content of the element with the result of the provided expression. It has the same meaning as that of the double curly markup, for example, {{expression}}.

Why would anyone like to use this directive when a less verbose alternative is available? This is because when the page is being compiled, there is a moment when the raw state of the expressions is shown. Since the directive is defined by the attribute of the element, it is invisible to the user. Here is an example of the ngBind directive usage:

index.html

<!doctype html>
<html ng-app="parking">  
  <head>
    <title>[Packt] Parking</title>
    <script src="angular.js"></script>
    <script>
      var parking = angular.module("parking", []);
      parking.controller("parkingCtrl", function ($scope) {
        $scope.appTitle = "[Packt] Parking";
      });
    </script>
  </head>
  <body ng-controller="parkingCtrl">
    <h3 ng-bind="appTitle"></h3>
  </body>
</html>

ngRepeat

The ngRepeat directive is really useful to iterate over arrays and objects. It can be used with any kind of element such as rows of a table, elements of a list, and even options of select.

We must provide a special repeat expression that describes the array to iterate over and the variable that will hold each item in the iteration. The most basic expression format allows us to iterate over an array, attributing each element to a variable:

variable in array

In the following code, we will iterate over the cars array and assign each element to the car variable:

index.html

<!doctype html>
<html ng-app="parking">
  <head>
    <title>[Packt] Parking</title>
    <script src="angular.js"></script>
    <script>
      var parking = angular.module("parking", []);
      parking.controller("parkingCtrl", function ($scope) {
        $scope.appTitle = "[Packt] Parking";

        $scope.cars = [];
      });
    </script>
  </head>
  <body ng-controller="parkingCtrl">
    <h3 ng-bind="appTitle"></h3>
    <table>
      <thead>
        <tr>
          <th>Plate</th>
          <th>Entrance</th>
        </tr>
      </thead>
      <tbody>
        <tr ng-repeat="car in cars">
          <td><span ng-bind="car.plate"></span></td>
          <td><span ng-bind="car.entrance"></span></td>
        </tr>
      </tbody>
    </table>
  </body>
</html>

ngModel

The ngModel directive attaches the element to a property in the scope, binding the view to the model. In this case, the element could be input (all types), select, or textarea.

  <input 
    type="text" 
    ng-model="car.plate" 
    placeholder="What's the plate?"
  />

There is an important advice regarding the use of this directive. We must pay attention to the purpose of the field that is using the ngModel directive. Every time the field is being part of the construction of an object, we must declare in which object the property should be attached. In this case, the object that is being constructed is a car, so we use car.plate inside the directive expression.

However, sometimes it might occur that there is an input field that is just used to change a flag, allowing the control of the state of a dialog or another UI component. In these cases, we may use the ngModel directive without any object, as far as it will not be used together with other properties or even persisted.

ngClick and other event directives

The ngClick directive is one of the most useful kinds of directives in the framework. It allows you to bind any custom behavior to the click event of the element. The following code is an example of the usage of the ngClick directive calling a function:

index.html

<!doctype html>
<html ng-app="parking">
  <head>
    <title>[Packt] Parking</title>
    <script src="angular.js"></script>
    <script>
      var parking = angular.module("parking", []);
      parking.controller("parkingCtrl", function ($scope) {
        $scope.appTitle = "[Packt] Parking";

        $scope.cars = [];
        
        $scope.park = function (car) {
          car.entrance = new Date();
          $scope.cars.push(car);
          delete $scope.car;
        };
      });
    </script>
  </head>
  <body ng-controller="parkingCtrl">
    <h3 ng-bind="appTitle"></h3>
    <table>
      <thead>
        <tr>
          <th>Plate</th>
          <th>Entrance</th>
        </tr>
      </thead>
      <tbody>
        <tr ng-repeat="car in cars">
          <td><span ng-bind="car.plate"></span></td>
          <td><span ng-bind="car.entrance"></span></td>
        </tr>
      </tbody>
    </table>
    <input 
      type="text" 
      ng-model="car.plate" 
      placeholder="What's the plate?"
    />
    <button ng-click="park(car)">Park</button>
  </body>
</html>

Here there is another pitfall. Inside the ngClick directive, we call the park function, passing the car as a parameter. As far as we have access to the scope through the controller, would not be easier if we just access it directly, without passing any parameter at all?

Keep in mind that we must take care of the coupling level between the view and the controller. One way to keep it low is by avoid reading the scope object directly from the controller, replacing this intention by passing everything it need by parameter from the view. It will increase the controller testability and also make the things more clear and explicitly.

Other directives that have the same behavior, but are triggered by other events, are ngBlur, ngChange, ngCopy, ngCut, ngDblClick, ngFocus, ngKeyPress, ngKeyDown, ngKeyUp, ngMousedown, ngMouseenter, ngMouseleave, ngMousemove, ngMouseover, ngMouseup, and ngPaste.

Filters

The filters are, associated with other technologies like directives and expressions, responsible for the extraordinary expressiveness of framework. It lets us easily manipulate and transform any value, not only combined with expressions inside a template, but also injected in other components like controllers and services.

It is really useful when we need to format date and money according to our current locale or even support the filtering feature of a grid component. Filters are the perfect answer to easily perform any data manipulating.

currency

The currency filter is used to format a number based on a currency. The basic usage of this filter is without any parameter:

{{ 10 | currency}}

The result of the evaluation will be the number $10.00, formatted and prefixed with the dollar sign. In order to achieve the correct output, in this case R$10,00 instead of R$10.00, we need to configure the Brazilian (PT-BR) locale, available inside the AngularJS distribution package. There, we may find locales to the most part of the countries and we just need to import it to our application such as:

<script src="js/lib/angular-locale_pt-br.js"></script>

After import the locale, we will not need to use the currency symbol anymore because it's already wrapped inside.

Besides the currency, the locale also defines the configuration of many other variables like the days of the week and months, very useful when combined with the next filter used to format dates.

date

The date filter is one of the most useful filters of the framework. Generally, a date value comes from the database or any other source in a raw and generic format. In this way, filters like that are essential to any kind of application.

Basically, we can use this filter by declaring it inside any expression. In the following example, we use the filter on a date variable attached to the scope.

{{ car.entrance | date }}

The output will be Dec 10, 2013. However, there are thousands of combinations that we can make with the optional format mask.

{{ car.entrance | date:'MMMM dd/MM/yyyy HH:mm:ss' }}

Using this format, the output changes to December 10/12/2013 21:42:10.

filter

Have you ever needed to filter a list of data? This filter performs exactly this task, acting over an array and applying any filtering criteria.

Now, let's include in our car parking application a field to search any parked car and use this filter to do the job.

index.html

<input 
  type="text" 
  ng-model="criteria" 
  placeholder="What are you looking for?"
/>
<table>
  <thead>
    <tr>
      <th></th>
      <th>Plate</th>
      <th>Color</th>
      <th>Entrance</th>
    </tr>
  </thead>
  <tbody>
    <tr 
      ng-class="{selected: car.selected}" 
      ng-repeat="car in cars | filter:criteria"
 >
   <td>
        <input 
          type="checkbox" 
          ng-model="car.selected"
        />
      </td>
      <td>{{car.plate}}</td>
      <td>{{car.color}}</td>
      <td>{{car.entrance | date:'dd/MM/yyyy hh:mm'}}</td>
    </tr>
  </tbody>
</table>

The result is really impressive. With an input field and the filter declaration we did the job.

Integrating the backend with AJAX

AJAX, also known as Asynchronous JavaScript and XML, is a technology that allows the applications to send and retrieve data from the server asynchronously, without refreshing the page. The $http service wraps the low-level interaction with the XMLHttpRequest object, providing an easy way to perform calls.

This service could be called by just passing a configuration object, used to set many important information like the method, the URL of the requested resource, the data to be sent, and many others:

$http({method: "GET", url: "/resource"})
  .success(function (data, status, headers, config, statusText) {
  })
  .error(function (data, status, headers, config, statusText) {
  });

To make it easier to use, there are the following shortcuts methods available for this service. In this case, the configuration object is optional.

$http.get(url, [config])
$http.post(url, data, [config])
$http.put(url, data, [config])
$http.head(url, [config])
$http.delete(url, [config])
$http.jsonp(url, [config])

Now, it’s time to integrate our parking application with the back-end by calling the resource cars with the method GET. It will retrieve the cars, binding it to the $scope object. In case of something went wrong, we are going to log it to the console.

controllers.js

parking.controller("parkingCtrl", function ($scope, $http) {
  $scope.appTitle = "[Packt] Parking";
   
  $scope.park = function (car) {
    car.entrance = new Date();
    $scope.cars.push(car);
    delete $scope.car;
  };

  var retrieveCars = function () {
    $http.get("/cars")
      .success(function(data, status, headers, config) {
        $scope.cars = data;
      })
      .error(function(data, status, headers, config) {
        switch(status) {
          case 401: {
            $scope.message = "You must be authenticated!"
            break;
          }
          case 500: {
            $scope.message = "Something went wrong!";
            break;
          }
        }
        console.log(data, status);  
      });
  };
  retrieveCars();
});

Summary

This article introduced you to the fundamentals of AngularJS in order to design and construct reusable, maintainable, and modular web applications.

Resources for Article:


Further resources on this subject:


Books to Consider

comments powered by Disqus