Table of Contents (Hide)

AngularJS Basics

(Not for the dummies)

Introduction

AngularJS (@ https://angularjs.org/) is a client-side JavaScript MVC (Model-View-Controller) Framework meant for implementing Single Page Architecture (SPA), maintained by Google and released in October 2010.

AngularJS is meant to replace jQuery, which is a simple but popular JavaScript library. AngularJS is more powerful than jQuery. However, it is much more difficult to learn and use, hence, not as popular as jQuery. jQuery also has a huge installed base with thousands of plug-ins to extend it functions.

The main features of AngularJS are:

  • MVC (Model-View-Controller) Framework: separating model, view and controller.
  • Single Page Application (SPA) Framework: via routing with multiple views to update a portion of a page (also via AJAX POST request with JSON payload).
  • Facilitate unit testing and end-to-end testing.

This results in fewer lines, less boilerplate, cleaner, reusable and testable codes.

AngularJS, combined with Bootstrap, lets you rapidly build webapp front-end that uses RESTful web services at the backend, for both desktops and mobile devices.

[TODO] more

Installation

The core AngularJS is a JavaScript file called angular.js (or the minified version angular.min.js) which can be downloaded from https://angularjs.org/ and included in your HTML files. Alternatively, you can use a copy provided by a CDN (Context Distribution Network).

AngularJS Template
1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<!-- ngTemplate.html -->
<html lang="en">
<head>
<meta charset="utf-8">
<title>YOUR TITLE HERE</title>
</head>
<body>
<p>Hello, world</p>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular.min.js"></script>
</body>
</html>
How It Works?
  1. Include the minified angular.min.js (or angular.js) under a <script> HTML tag. In the above example, I used the Google's CDN, but you can also serve your local copy.
  2. You can place the <script> tag in the <head> section; or just before the end of body section (</body>) for better responsiveness.

AngularJS By Examples

I assume that you know HTML5/CSS3 and JavaScript. If not, read those up first!!!

Example 1: Data Binding

ngEg_DataBind.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<!-- ngEg_DataBind.html -->
<html lang="en">
<head>
<meta charset="utf-8">
<title>AngularJS Example: Data Binding</title>
</head>
<body ng-app>
<input type="text" ng-model="yourName" placeholder="Enter your name">
<p>Hello, <span ng-bind="yourName"></span></p>
<p>Hello again, {{ yourName }}</p>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular.min.js"></script>
</body>
</html>

Load the HTML file on a web browser (e.g., Chrome, Firefox). Enter something on the input text field, and observe the results.

How It Works?
  1. In line 8, we bind the DOM element body as an AngularJS app module via a custom HTML attribute ng-app. (You can also use data-ng-app. The ng-app will fail HTML5 validation, as HTML5 does not define such an attribute. On the other hand, HTML5 defines data-* as so-called custom attributes, which will not be validated.)
  2. AngularJS binds to HTML DOM elements via ng-* custom attributes, known as ng-directives. In this example, we use:
    • ng-app: bind the DOM element to an AngularJS app module (Line 8).
    • ng-model: define a variable name (or data model name) and bind to the value of the HTML controls (in this example, the <input type='text'> field) (Line 9).
    • ng-bind: bind the DOM element's content (or innerHTML) to a ng-model (Line 10).
  3. This example illustrates data binding between the value of an input field and the content of a DOM element. We named the input field via an HTML attribute ng-model (Line 9). We then bind the input value to the content of a DOM element via ng-bind (Line 10). Once bound, whenever the input value changes, AngularJS updates the bound element and refreshes the UI.
  4. AngularJS further simplifies the binding syntax to {{ ... }} (Line 11). This is known as AngularJS's expression, which can be used to evaluate any JavaScript expression (AngularJS is JavaScript!) containing literals, objects, arrays, operators, variables and methods. For example {{ 1 + 2 + 3 }}, {{ "Hello" + ", " + "world" }}.

Example 2: Controller

An controller can be used to control the application, such as binding values to views.

ngEg_Controller.html
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
<!doctype html>
<!-- ngEg_Controller.html -->
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Example: AngularJS Controller</title>
</head>
 
<body ng-app="myApp" ng-controller="myCtrl">
  <h1>Hello, {{ name }}</h1>
  <button ng-click="click()">Click Me!</button>
 
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular.min.js"></script>
<script>
// Define an AngularJS app module
var app = angular.module('myApp', []);
 
// Define the controller for this app module
app.controller('myCtrl', function($scope) {
   $scope.name = "Peter";
 
   $scope.click = function() {
      if ($scope.name == "Peter") {
         $scope.name = "Paul";
      } else {
         $scope.name = "Peter";
      }
   }
});
</script>
</body>
</html>
How It Works?
  1. In this example, we attach an AngularJS app to the DOM element body and named it myApp (Line 9). The name is needed for referencing the app.
  2. We also attach a controller called myCtrl via ng-controller (Line 9). We will use the controller to manipulate AngularJS variables and define event handling functions.
  3. In Line 16, we construct our AngularJS app (myApp), which does not have any dependencies (empty array).
  4. In Line 19, we setup the controller (myCtrl) via app.controller(controller-name, controller-function). A controller must belong to an app. We inject the $scope object into the controller, which contains all the variables and methods of this app module. We can then use $scope.varname to bind to AngularJS variable varname in the HTML view (Line 20).
  5. Dependency Injection: AngularJS invokes the controller function via an injector, which is responsible to inject the $scope object (having the same name as the function parameter) into the controller function. This follows the design pattern called Dependency Injection (DI). There are 3 ways to annotate dependencies:
    1. Using Inline Array Annotation (the preferred way): e.g.,
      app.controller('myCtrl', ['$scope', '$http', function($scope, $http) {...}]);
      Use an array consisting of a list of dependency names (in string) followed by the function. The order of the dependency names corresponding to the order of the function parameters.
    2. Using $inject Annotation: e.g.,
      var myCtrl = function($scope, $http) {...};
      myCtrl.$inject = ['$scope', '$http'];
      myApp.controller('myCtrl', myCtrl);
    3. Using Implicit Annotation: the simplest where the dependency names are the same as the function parameters, as in this example. However, you CANNOT minify your codes, if you use implicit annotation, as the dependency names will get renamed.
  6. The $scope binds the HTML View and the JavaScript Controller. The $scope is a JavaScript object containing variables and methods. Inside the JavaScript Controller, you must use $scope.attr-name; while in HTML view, you simply use attr-name.
  7. In Line 11, we attach a event handling function click() to the button via ng-click. We then define the function inside the controller via $scope.click = function() {...} (Line 22).

Example 3: Two-way Data Binding and Filters

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
<!DOCTYPE html>
<!-- ngEg_DataBind2.html -->
<html lang="en">
<head>
<meta charset="utf-8">
<title>AngularJS Example: 2-way Binding</title>
</head>
 
<body ng-app="myApp" ng-controller="myCtrl">
  <input type="text" ng-model="name" placeholder="Enter your name">
  <p>Hello, {{ name | uppercase }}</p>
  <button ng-click="click()">Click Me!</button>
 
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular.min.js"></script>
<script>
// Define an AngularJS app module
var app = angular.module('myApp', []);
 
// Define the controller for this app module
app.controller('myCtrl', ['$scope', function($scope) {   // Using Inline Array Annotation for DI
   // Modify the variable, onclick
   $scope.click = function() {
      $scope.name = "Peter";
   }
}]);
</script>
</body>
</html>
How It Works?
  1. In Line 10, we use ng-model to bind the value of the input control to the variable name.
  2. In Line 11, we use the expression to bind the value of the variable name to the DOM element. Whenever the value of the input control changes, the content of the bound DOM element also changes.
  3. The binding is two-way. When the value of the variable changes (thru the click() function), the value of the input control also changes.
AngularJS Filters

You can apply filter to format data for display in an expression via the pipe '|' symbol, e.g., {{ name | uppercase }} (Line 11).

The frequently-used filters are:

  • uppercase, lowercase
  • currency, date, number:
  • json: format an JavaScript object to a JSON string

Example 4: ng-repeat and more Filters

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
44
45
46
<!doctype html>
<!-- ngEg_repeat.html -->
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Example: AngularJS ng-repeat</title>
</head>
 
<body ng-app="myApp" ng-controller="myCtrl">
  <h1>My Cafe</h1>
  <ol>
    <!-- Apply AngularJS filter 'currency' -->
    <li ng-repeat="item in cafe">{{ item.desc + ', ' + (item.price | currency) }}</li>
  </ol>
  <hr>
  <ol>
    <!-- Apply AngularJS filter 'orderBy' -->
    <li ng-repeat="item in cafe | orderBy:'price'">{{ item.desc + ', ' + item.price }}</li>
  </ol>
  <hr>
  <ol>
    <!-- Apply AngularJS filter 'limitTo' -->
    <li ng-repeat="item in cafe | limitTo:'2'">{{ item.desc + ', ' + item.price }}</li>
  </ol>
  <hr>
  <ol>
    <!-- Apply AngularJS filter 'filter' to search for item containing 'p' -->
    <li ng-repeat="item in cafe | filter:'p'">{{ item.desc + ', ' + item.price }}</li>
  </ol>
 
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular.min.js"></script>
<script>
// Define an AngularJS app module
var app = angular.module('myApp', []);
 
// Define the controller for this app module
app.controller('myCtrl', ['$scope', function($scope) {
   $scope.cafe = [
      {desc: 'Cappuccino', price: 3.29},
      {desc: 'Cafe Latte', price: 3.99},
      {desc: 'Espresso', price: 3.19}
   ];
}]);
</script>
</body>
</html>
How It Works?
  1. [TODO]

Example 5: Form and Input Validation

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
44
45
<!doctype html>
<!-- ngEg_Form.html -->
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Example: AngularJS Form and Input Validation</title>
</head>
 
<body ng-app="myApp" ng-controller="myCtrl">
  <form name="thisForm" novalidate ng-submit="thisForm.$dirty && thisForm.$valid && submit()">
    <label for="username">Username:</label>
    <input type="text" id="username" name="username" ng-model="user" required><br>
    <!-- Display error message, if any (reference via 'name' attribute) -->
    <div ng-show="thisForm.username.$touched && thisForm.username.$invalid">
       <span ng-show="thisForm.username.$error.required">This field is required</span>
    </div>
 
    <label for="password">Password:</label>
    <input type="password" name="password" ng-model="passwd" required ng-pattern="/[a-zA-Z]{4,16}/"><br>
    <div ng-show="thisForm.password.$touched && thisForm.password.$invalid">
       <span ng-show="thisForm.password.$error.required">This field is required</span>
       <span ng-show="thisForm.password.$error.pattern">4 to 16 letters</span>
    </div>
 
    <input type="submit">
  </form>
  <hr />
  <div >{{ message }}</div>
 
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular.min.js"></script>
<script>
// Define an AngularJS app
var app = angular.module('myApp', []);
 
// Define the controller
app.controller('myCtrl', ['$scope', '$http', function($scope, $http) {
   $scope.message = "Testing AngularJS";
   $scope.submit = function() {
      console.log("submitting ajax post...");
      // To submit an AJAX POST request with JSON payload
   };
}]);
</script>
</body>
</html>
How It Works?
  1. In Line 9, we attach an AngularJS app module (called myApp) and controller (called myCtrl) to the DOM element body. Line 33 defines the app module and Line 36 defines the controller function.
  2. AngularJS can perform client-side input validation. In Line 10, we disable HTML5 validation via attribute novalidate (we let AngularJS to do validation).
  3. For the 'username' field, the validator required is applied (Line 12). In Line 14-16, the boolean attribute ng-show is used to display the error messages only if error occurs. The input field is referenced via the form-name.field-name (using the name attribute instead of id attribute?!) For fields, $touch (Line 14) is true if the field has gain and lost focus; $invalid (Line 14) is true if validation fails. $error.required (Line 15) is true if validator required fails.
  4. For the 'password' field, two validators are specified: required and a regular expression (4-16 characters of a-z or A-Z) via ng-pattern (Line 19). In Line 20-22, the boolean attribute ng-show is used to display the error messages only if error occurs.
  5. To disable 'submit' if input validation fails, we check the $dirty (at least one of the fields have been changed) and $valid (all validations for all fields pass) before invoking the submit() function (Line 10).

Example 6: Using ngMessages to display error messages

You can use add-on module ngMessages to simplify the display of error messages, e.g.,

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
44
45
46
<!doctype html>
<!-- ngEg_ngMessage.html -->
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Example: Using 'ngMessages' module to display error messages</title>
</head>
 
<body ng-app="myApp" ng-controller="myCtrl">
  <form name="thisForm" novalidate ng-submit="thisForm.$dirty && thisForm.$valid && submit()">
    <label for="username">Username:</label>
    <input type="text" id="username" name="username" ng-model="user" required><br>
    <!-- Display error message, if any (reference via 'name' attribute) -->
    <div ng-messages="thisForm.username.$error" ng-if="thisForm.username.$touched">
      <p ng-message="required">This field is required.</p>
    </div>
 
    <label for="password">Password:</label>
    <input type="password" name="password" ng-model="passwd" required ng-pattern="/[a-zA-Z]{4,16}/"><br>
    <div ng-messages="thisForm.password.$error" ng-if="thisForm.password.$touched">
      <p ng-message="pattern">4 to 16 letters.</p>
      <p ng-message="required">This field is required.</p>
    </div>
 
    <input type="submit">
  </form>
  <hr />
  <div >{{ message }}</div>
 
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular-messages.js"></script>
<script>
// Define an AngularJS app
app = angular.module('myApp', ['ngMessages']);
 
// Define the controller
app.controller('myCtrl', ['$scope', '$http', function($scope, $http) {
   $scope.message = "Testing AngularJS";
   $scope.submit = function() {
      console.log("submitting ajax post...");
      // To submit an AJAX POST request with JSON payload
   };
}]);
</script>
</body>
</html>
How It Works?
  1. The AngularJS module ngMessages is provided in separate JavaScript file and need to be loaded (Line 31).
  2. The highlight lines show how to use 'ngMessages'. We further use a 'ng-if to check if the field has been touched.

Example 7: AJAX POST via $http Service (Server Required)

To implement Single Page Architecture (SPA), AngularJS sends AJAX request (with JSON payload) to the server (usually via Restful web service API). It then receives response (with JSON data) and update the appropriate DOM element of the page, instead of reloading the entire page.

Client-side Program: ngEx_Ajax.html
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<!doctype html>
<!-- ngEg_Ajax.html -->
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Example: AngularJS Ajax POST</title>
</head>
 
<body ng-app="myApp" ng-controller="myCtrl">
  <h1>Hello,</h1>
 
  <form name="thisForm" novalidate ng-submit="thisForm.$dirty && thisForm.$valid && submit()">
     Username: <input type="text" name="username" ng-model="user" required ng-pattern="/[a-zA-Z]{4,16}/"><br>
     <!-- Display error message, if any (reference via 'name' attribute) -->
     <div ng-show="thisForm.username.$touched && thisForm.username.$invalid">
        <span ng-show="thisForm.username.$error.required">This field is required</span>
        <span ng-show="thisForm.username.$error.pattern">4 to 16 letters</span>
     </div>
     <input type="submit">
  </form>
  <hr />
  <div >{$ message $}</div>
 
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular.min.js"></script>
<script>
// Define an AngularJS app
var app = angular.module('myApp', []);
 
// Change the delimiters for AngularJS to {$ ... $}
app.config(function($interpolateProvider) {
    $interpolateProvider.startSymbol('{$').endSymbol('$}');
});
 
// Define the controller
app.controller('myCtrl', ['$scope', '$http', function($scope, $http) {
   $scope.message = "Testing AngularJS";
 
   $scope.submit = function() {
      // Send an AJAX POST request with JSON payload for submit
      console.log("submitting ajax post...");
      $http({
         method: 'POST',
         url: '/',
         data: {
            user: $scope.user  // $http handles jsonify
         },
         headers: {
            'X-Requested-With': 'XMLHttpRequest',             // AJAX request 
            'Content-Type': 'application/json;charset=utf-8'  // JSON payload
         }
      }).then(function success(response) {  // Response status code 2xx and 3xx
            console.log("success");
            // response.status keeps the numeric status code
            // response.data keeps the return JSON data from the server
            $scope.message = response.status + ": " + JSON.stringify(response.data);
         }, function error(response) {      // Response status code 4xx and 5xx
            console.log("error");
            $scope.message = response.status + ": " + JSON.stringify(response.data);
         });
   };
}]);
</script>
</body>
</html>
How It Works?
  1. In Line 9, we attach an AngularJS app module (called myApp) and controller (called myCtrl) to the DOM element body. Line 27 defines the app module and Line 35 defines the controller function.
  2. In Line 30, we change the AngularJS expression from the default {{ expression }} to {$ expression $} (because it crashes with our server-side template notation).
  3. In Line 35, we inject both $scope object and $http service into the controller. $scope object is used to access all the variables and methods; while $http service is used to send an HTTP request, in this case, a AJAX POST request with JSON payload. In Line 47, we added header 'X-Requested-With': 'XMLHttpRequest' to denote AJAX request, and 'Content-Type': 'application/json;charset=utf-8' to specify the request payload MIME type.
  4. In Line 51, the .then() defines two functions: a success function (triggered by response status code of 2xx and 3xx) and a failure function (for response status code of 4xx and 5xx).
  5. AngularJS can perform client-side input validation. In Line 12, we disable HTML5 validation via attribute novalidate (we let AngularJS to do validation).
  6. Two validators are specified: required and a regular expression (4-16 characters of a-z or A-Z) via ng-pattern (Line 13). In Line 15-17, the boolean attribute ng-show is used to display the error messages only if error occurs. The input field is referenced via the form-name.field-name (using the name attribute instead of id attribute?!) For fields, $touch (Line 15) is true if the field has gain and lost focus; $invalid (Line 15) is true if validation fails. $error.required (Line 16) is true if validator required fails; $error.pattern (Line 17) is true if validator ng-pattern fails.
  7. To disable submit if validation fails, we check $dirty (at least one of the fields has been changed) and $valid (all validations for all fields pass) before invoking the submit() function (Line 11).
Server-side Program: ngEx_Ajax.py

This server side program was written in Python Flask to process the AJAX POST request. You can use any server-side technologies to process the AJAX POST request.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
"""ngEx_Ajax.py"""
from flask import Flask, render_template, request
 
app = Flask(__name__)
 
@app.route("/", methods=['GET', 'POST'])
def main():
    if request.method == 'POST' and request.is_xhr:  # AJAX POST request?
        json_data = request.get_json()   # Get POST JSON payload
        print("json: " + str(json_data))
 
        username = json_data.get('user')
        if username == 'Peter':
            return "Your are Peter", 200      # Success
        else:
            return "Your are NOT Peter", 400  # Failure
 
    # For the initial GET request to render the page
    return render_template('ngEg_Ajax.html')
 
if __name__ == "__main__":
    app.run(debug=True)
How It Works?
  1. [TODO]

Example 8: Routing and Multiple Views in SPA

Routing is crucial in the implementation of Single Page Architecture (SPA) with multiple views rendered on a single page.

ngEg_routeCtrl.html
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
<!doctype html>
<!-- ngEg_routeCtrl.html -->
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Example: AngularJS Routing and Multiple Views</title>
</head>
 
<body ng-app="myApp">
<ul>
<li><a href="#!/">Main</a></li>
<li><a href="#!/1">One</a></li>
<li><a href="#!/2">Two</a></li>
</ul>
<p></p>
 
<div ng-view></div>
 
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular-route.js"></script>
<script>
// Define an AngularJS app module
var app = angular.module('myApp', ['ngRoute']);  // depends on 'ngRoute' module
 
// Define the url-route/view for rendering in DOM element marked with 'ng-view'
app.config(['$locationProvider', '$routeProvider', function($locationProvider, $routeProvider) {
    $locationProvider.hashPrefix('!');    // Relative URL is: #!/some/path
 
    $routeProvider
    .when("/", {      // #!/
        templateUrl: "ngEg_route0.html"   // Relative to current folder
    })
    .when("/1", {     // #!/1
        templateUrl: "ngEg_route1.html"
    })
    .when("/2", {     // #!/2
        templateUrl: "ngEg_route2.html"
    })
    .otherwise("/");  // #!/
}]);
</script>
</body>
</html>
How It Works?
  1. Routing is provided via a separate AngularJS module called ngRoute (in angular-route.js), that needs to be linked to the HTML page separately (Line 20).
  2. The $routeProvider.when() defines the URL routes and their views (templateUrl). The $routeProvider.otherwise() defines the default view. The page in templateUrl will be rendered in DOM element marked with ng-view.
  3. The $locationProvider.hashPrefix('!') sets the url prefix (after the #, which references an anchor point inside an HTML page). As the result, the url route '/1' (marked by $routeProvider) is to be referenced as '#!/1' (in <a href='#!/1'>)

Example 9: Events

When the mouse pointer moves over a DOM element, these events occur in this order:

  1. ng-mouseenter
  2. ng-mouseover
  3. ng-mousemove
  4. ng-mouseleave

These mouse events occur for a mouse click on a DOM element, in this order:

  1. ng-mousedown
  2. ng-mouseup
  3. ng-mouseclick

Key events:

  1. ng-keydown
  2. ng-keyup
  3. ng-keypress

Copy/Paste

  • ng-copy
  • ng-cut
  • ng-paste

Other events:

  • ng-blur
  • ng-focus
  • ng-change
  • ng-click
  • ng-dblclick

[TODO]

Example 10: Services

  • $location Service:
  • $timeout Service:
  • $interval Service:

[TODO]

Example 11: With Bootstrap

[TODO]

Example 12: Separating Model, View and Controller (MVC)

[TODO]

Example 13: Unit Testing via Karma and End-to-end testing via Protractor

[TODO]

REFERENCES & RESOURCES
  1. Angular JS mother site @ https://angularjs.org/.