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?
- Include the minified
angular.min.js
(orangular.js
) under a<script>
HTML tag. In the above example, I used the Google's CDN, but you can also serve your local copy. - 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?
- In line 8, we bind the DOM element
body
as an AngularJS app module via a custom HTML attributeng-app
. (You can also usedata-ng-app
. Theng-app
will fail HTML5 validation, as HTML5 does not define such an attribute. On the other hand, HTML5 definesdata-*
as so-called custom attributes, which will not be validated.) - AngularJS binds to HTML DOM elements via
ng-*
custom attributes, known asng-
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 (orinnerHTML
) to ang-model
(Line 10).
- 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 viang-bind
(Line 10). Once bound, whenever the input value changes, AngularJS updates the bound element and refreshes the UI. - 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?
- In this example, we attach an AngularJS app to the DOM element
body
and named itmyApp
(Line 9). The name is needed for referencing the app. - We also attach a controller called
myCtrl
viang-controller
(Line 9). We will use the controller to manipulate AngularJS variables and define event handling functions. - In Line 16, we construct our AngularJS app (
myApp
), which does not have any dependencies (empty array). - In Line 19, we setup the controller (
myCtrl
) viaapp.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 variablevarname
in the HTML view (Line 20). - 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:- 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. - Using
$inject
Annotation: e.g.,var myCtrl = function($scope, $http) {...}; myCtrl.$inject = ['$scope', '$http']; myApp.controller('myCtrl', myCtrl);
- 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.
- Using Inline Array Annotation (the preferred way): e.g.,
- 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 useattr-name
. - In Line 11, we attach a event handling function
click()
to the button viang-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?
- In Line 10, we use
ng-model
to bind the value of the input control to the variablename
. - 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. - 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?
- [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?
- In Line 9, we attach an AngularJS app module (called
myApp
) and controller (calledmyCtrl
) to the DOM elementbody
. Line 33 defines the app module and Line 36 defines the controller function. - AngularJS can perform client-side input validation. In Line 10, we disable HTML5 validation via attribute
novalidate
(we let AngularJS to do validation). - For the
'username'
field, the validatorrequired
is applied (Line 12). In Line 14-16, the boolean attributeng-show
is used to display the error messages only if error occurs. The input field is referenced via theform-name.field-name
(using thename
attribute instead ofid
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 validatorrequired
fails. - For the
'password'
field, two validators are specified:required
and a regular expression (4-16 characters of a-z or A-Z) viang-pattern
(Line 19). In Line 20-22, the boolean attributeng-show
is used to display the error messages only if error occurs. - 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 thesubmit()
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?
- The AngularJS module
ngMessages
is provided in separate JavaScript file and need to be loaded (Line 31). - 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?
- In Line 9, we attach an AngularJS app module (called
myApp
) and controller (calledmyCtrl
) to the DOM elementbody
. Line 27 defines the app module and Line 35 defines the controller function. - In Line 30, we change the AngularJS expression from the default
{{ expression }}
to{$ expression $}
(because it crashes with our server-side template notation). - 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. - 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). - AngularJS can perform client-side input validation. In Line 12, we disable HTML5 validation via attribute
novalidate
(we let AngularJS to do validation). - Two validators are specified:
required
and a regular expression (4-16 characters of a-z or A-Z) viang-pattern
(Line 13). In Line 15-17, the boolean attributeng-show
is used to display the error messages only if error occurs. The input field is referenced via theform-name.field-name
(using thename
attribute instead ofid
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 validatorrequired
fails;$error.pattern
(Line 17) is true if validatorng-pattern
fails. - 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 thesubmit()
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?
- [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?
- Routing is provided via a separate AngularJS module called
ngRoute
(inangular-route.js
), that needs to be linked to the HTML page separately (Line 20). - The
$routeProvider.when()
defines the URL routes and their views (templateUrl
). The$routeProvider.otherwise()
defines the default view. The page intemplateUrl
will be rendered in DOM element marked withng-view
. - 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:
- ng-mouseenter
- ng-mouseover
- ng-mousemove
- ng-mouseleave
These mouse events occur for a mouse click on a DOM element, in this order:
- ng-mousedown
- ng-mouseup
- ng-mouseclick
Key events:
- ng-keydown
- ng-keyup
- 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- Angular JS mother site @ https://angularjs.org/.