AngularJS W3Schools-1
AngularJS W3Schools-1
Previous
Next Chapter
AngularJS is a JavaScript framework. It can be added to an HTML page with a
<script> tag.
AngularJS extends HTML attributes with Directives, and binds data to HTML with
Expressions.
AngularJS Example
<!DOCTYPE html>
<html>
<head>
<scriptsrc="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></scri
pt>
</head>
<body>
<div ng-app="">
<p>Name: <input type="text" ng-model="name"></p>
<p ng-bind="name"></p>
</div>
</body>
</html>
Try it Yourself
Example explained:
AngularJS starts automatically when the web page has loaded.
The ng-app directive tells AngularJS that the <div> element is the "owner" of an
AngularJS application.
The ng-model directive binds the value of the input field to the application variable
name.
The ng-bind directive binds the innerHTML of the <p> element to the application
variable name.
AngularJS Directives
As you have already seen, AngularJS directives are HTML attributes with an ng prefix.
The ng-init directive initialize AngularJS application variables.
AngularJS Example
<div ng-app="" ng-init="firstName='John'">
<p>The name is <span ng-bind="firstName"></span></p>
</div>
Try it Yourself
You can use data-ng-, instead of ng-, if you want to make your page HTML5 valid.
AngularJS Example
<div data-ng-app="" data-ng-init="firstName='John'">
You will learn a lot more about directives later in this tutorial.
AngularJS Expressions
AngularJS expressions are written inside double braces: {{ expression }}.
AngularJS expressions bind data to HTML the same way as the ng-bind directive.
AngularJS will "output" data exactly where the expression is written.
AngularJS Example
<!DOCTYPE html>
<html>
<head>
<scriptsrc="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></scri
pt>
</head>
<body>
<div ng-app="">
<p>My first expression: {{ 5 + 5 }}</p>
</div>
</body>
</html>
Try it Yourself
AngularJS Controllers
AngularJS applications are controlled by controllers.
The ng-controller directive defines the controller.
The controller code will execute when the page loads.
AngularJS Example
<div ng-app="" ng-controller="personController">
First Name: <input type="text" ng-model="firstName"><br>
Last Name: <input type="text" ng-model="lastName"><br>
<br>
Full Name: {{firstName + " " + lastName}}
</div>
<script>
function personController($scope) {
$scope.firstName = "John";
$scope.lastName = "Doe";
}
</script>
Try it Yourself
You will learn a lot more about controllers later in this tutorial.
AngularJS Expressions
Previous
Next Chapter
AngularJS binds data to HTML using Expressions.
AngularJS Expressions
AngularJS expressions are written inside double braces: {{ expression }}.
AngularJS expressions binds data to HTML the same way as the ng-bind directive.
AngularJS will "output" data exactly where the expression is written.
AngularJS expressions are much like JavaScript expressions: They can contain
literals, operators, and variables.
Example {{ 5 + 5 }} or {{ firstName + " " + lastName }}
AngularJS Example
<!DOCTYPE html>
<html>
<head>
<script
src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
</head>
<body>
<div ng-app="">
<p>My first expression: {{ 5 + 5 }}</p>
</div>
</body>
</html>
Try it Yourself
If you remove the ng-app directive, HTML will display the expression as it is, without
solving it:
AngularJS Example
<!DOCTYPE html>
<html>
<head>
<script
src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
</head>
<body>
<div>
<p>My first expression: {{ 5 + 5 }}</p>
</div>
</body>
</html>
Try it Yourself
AngularJS Numbers
AngularJS numbers are like JavaScript numbers:
AngularJS Example
<div ng-app="" ng-init="quantity=1;cost=5">
<p>Total in dollar: {{ quantity * cost }}</p>
</div>
Try it Yourself
AngularJS Example
<div ng-app="" ng-init="quantity=1;cost=5">
<p>Total in dollar: <span ng-bind="quantity * cost"></span></p>
</div>
Try it Yourself
Using ng-init is not very common. You will learn a better way to initialize data in
the chapter about controllers.
AngularJS Strings
AngularJS strings are like JavaScript strings:
AngularJS Example
<div ng-app="" ng-init="firstName='John';lastName='Doe'">
<p>The name is {{ firstName + " " + lastName }}</p>
</div>
Try it Yourself
AngularJS Example
<div ng-app="" ng-init="firstName='John';lastName='Doe'">
<p>The name is <span ng-bind="firstName + ' ' + lastName"></span></p>
</div>
Try it Yourself
AngularJS Objects
6
AngularJS Example
<div ng-app="" ng-init="person={firstName:'John',lastName:'Doe'}">
<p>The name is {{ person.lastName }}</p>
</div>
Try it Yourself
AngularJS Example
<div ng-app="" ng-init="person={firstName:'John',lastName:'Doe'}">
<p>The name is <span ng-bind="person.lastName"></span></p>
</div>
Try it Yourself
AngularJS Arrays
AngularJS arrays are like JavaScript arrays:
AngularJS Example
<div ng-app="" ng-init="points=[1,15,19,2,40]">
<p>The points are {{ points[2] }}</p>
</div>
Try it Yourself
AngularJS Example
<div ng-app="" ng-init="points=[1,15,19,2,40]">
<p>The points are <span ng-bind="points[2]"></span></p>
</div>
Try it Yourself
AngularJS Directives
Previous
Next Chapter
AngularJS lets you extend HTML with new attributes called Directives.
AngularJS Directives
AngularJS directives are extended HTML attributes with the prefix ng-.
The ng-app directive initializes an AngularJS application.
The ng-init directive initialize application data.
The ng-model directive binds the value of HTML controls (input, select, textarea) to
application data.
AngularJS Example
<div ng-app="" ng-init="firstName='John'">
<p>Name: <input type="text" ng-model="firstName"></p>
<p>You wrote: {{ firstName }}</p>
</div>
Try it Yourself
The ng-app directive also tells AngularJS that the <div> element is the "owner" of the
AngularJS application.
Data Binding
The {{ firstName }} expression, in the example above, is an AngularJS data binding
expression.
Data binding in AngularJS, synchronizes AngularJS expressions with AngularJS data.
8
AngularJS Example
<div ng-app="" ng-init="quantity=1;price=5">
Quantity: <input type="number" ng-model="quantity">
Costs: <input type="number" ng-model="price">
Total in dollar: {{ quantity * price }}
</div>
Try it Yourself
Using ng-init is not very common. You will learn how to initialize data in the
chapter about controllers.
AngularJS Example
<div ng-app="" ng-init="names=['Jani','Hege','Kai']">
<ul>
<li ng-repeat="x in names">
{{ x }}
</li>
</ul>
</div>
Try it Yourself
AngularJS Example
<div ng-app="" ng-init="names=[
{name:'Jani',country:'Norway'},
{name:'Hege',country:'Sweden'},
{name:'Kai',country:'Denmark'}]">
<ul>
<li ng-repeat="x in names">
{{ x.name + ', ' + x.country }}
</li>
</ul>
</div>
Try it Yourself
AngularJS is perfect for database CRUD (Create Read Update Delete) applications.
Just imagine if these objects were records from a database.
10
AngularJS Controllers
Previous
Next Chapter
AngularJS controllers control the data of AngularJS applications.
AngularJS controllers are regular JavaScript Objects.
AngularJS Controllers
AngularJS applications are controlled by controllers.
The ng-controller directive defines the application controller.
A controller is a JavaScript Object, created by a standard JavaScript object
constructor.
AngularJS Example
<div ng-app="" ng-controller="personController">
First Name: <input type="text" ng-model="firstName"><br>
Last Name: <input type="text" ng-model="lastName"><br>
<br>
Full Name: {{firstName + " " + lastName}}
</div>
<script>
function personController($scope) {
$scope.firstName="John",
11
$scope.lastName="Doe"
}
</script>
Try it Yourself
Application explained:
The AngularJS application is defined by ng-app. The application runs inside a <div>.
The ng-controller directive names the controller object.
The personController function is a standard JavaScript object constructor.
AngularJS will invoke personController with a $scope object.
In AngularJS, $scope is the application object (the owner of application variables and
functions).
The personController creates two properties (variables) in the scope (firstName
andlastName).
The ng-model directives bind the input fields to the controller properties (firstName
and lastName).
Controller Methods
The example above demonstrated a controller object with two properties: lastName and
firstName.
A controller can also have methods (functions as object properties):
AngularJS Example
<div ng-app="" ng-controller="personController">
First Name: <input type="text" ng-model="firstName"><br>
Last Name: <input type="text" ng-model="lastName"><br>
<br>
Full Name: {{fullName()}}
</div>
<script>
function personController($scope) {
$scope.firstName = "John",
$scope.lastName = "Doe",
$scope.fullName = function() {
12
AngularJS Example
<div ng-app="" ng-controller="personController">
First Name: <input type="text" ng-model="firstName"><br>
Last Name: <input type="text" ng-model="lastName"><br>
<br>
Full Name: {{firstName + " " + lastName}}
</div>
<script src="personController.js"></script>
Try it Yourself
Another Example
For the next example we will create a new controller file:
function namesController($scope) {
$scope.names = [
{name:'Jani',country:'Norway'},
{name:'Hege',country:'Sweden'},
{name:'Kai',country:'Denmark'}
];
}
And then use the controller file in an application:
AngularJS Example
<div ng-app="" ng-controller="namesController">
13
<ul>
<li ng-repeat="x in names">
{{ x.name + ', ' + x.country }}
</li>
</ul>
</div>
<script src="namesController.js"></script>
Try it Yourself
AngularJS Filters
Previous
Next Chapter
Filters can be added to expressions and directives using a pipe character.
AngularJS Filters
AngularJS filters can be used to transform data:
Filter
Description
currency
filter
lowercase
orderBy
uppercase
AngularJS Example
14
AngularJS Example
<div ng-app="" ng-controller="personController">
<p>The name is {{ lastName | lowercase }}</p>
</div>
Try it Yourself
AngularJS Example
<div ng-app="" ng-controller="costController">
<input type="number" ng-model="quantity">
<input type="number" ng-model="price">
<p>Total = {{ (quantity * price) | currency }}</p>
</div>
Try it Yourself
AngularJS Example
<div ng-app="" ng-controller="namesController">
15
<ul>
<li ng-repeat="x in names | orderBy:'country'">
{{ x.name + ', ' + x.country }}
</li>
</ul>
<div>
Try it Yourself
Filtering Input
An input filter can be added to a directive with a pipe character (|) and filter followed by
a colon and a model name.
The filter filter selects a subset of an array:
AngularJS Example
<div ng-app="" ng-controller="namesController">
<p><input type="text" ng-model="test"></p>
<ul>
<li ng-repeat="x in names | filter:test | orderBy:'country'">
{{ (x.name | uppercase) + ', ' + x.country }}
</li>
</ul>
</div>
Try it Yourself
AngularJS XMLHttpRequest
Previous
Next Chapter
$http is an AngularJS service for reading data from remote servers.
http://www.w3schools.com/website/Customers_JSON.php
16
[
{
"Name" : "Alfreds Futterkiste",
"City" : "Berlin",
"Country" : "Germany"
},
{
"Name" : "Berglunds snabbkp",
"City" : "Lule",
"Country" : "Sweden"
},
{
"Name" : "Centro comercial Moctezuma",
"City" : "Mxico D.F.",
"Country" : "Mexico"
},
{
"Name" : "Ernst Handel",
"City" : "Graz",
"Country" : "Austria"
},
{
"Name" : "FISSA Fabrica Inter. Salchichas S.A.",
"City" : "Madrid",
"Country" : "Spain"
},
{
"Name" : "Galera del gastrnomo",
"City" : "Barcelona",
"Country" : "Spain"
},
{
"Name" : "Island Trading",
"City" : "Cowes",
"Country" : "UK"
},
{
"Name" : "Kniglich Essen",
"City" : "Brandenburg",
"Country" : "Germany"
},
{
"Name" : "Laughing Bacchus Wine Cellars",
"City" : "Vancouver",
"Country" : "Canada"
},
{
"Name" : "Magazzini Alimentari Riuniti",
"City" : "Bergamo",
"Country" : "Italy"
},
{
17
"Name" : "North/South",
"City" : "London",
"Country" : "UK"
},
{
"Name" : "Paris spcialits",
"City" : "Paris",
"Country" : "France"
},
{
"Name" : "Rattlesnake Canyon Grocery",
"City" : "Albuquerque",
"Country" : "USA"
},
{
"Name" : "Simons bistro",
"City" : "Kbenhavn",
"Country" : "Denmark"
},
{
"Name" : "The Big Cheese",
"City" : "Portland",
"Country" : "USA"
},
{
"Name" : "Vaffeljernet",
"City" : "rhus",
"Country" : "Denmark"
},
{
"Name" : "Wolski Zajazd",
"City" : "Warszawa",
"Country" : "Poland"
}
]
AngularJS $http
AngularJS $http is a core service for reading data from web servers.
$http.get(url) is the function to use for reading server data.
AngularJS Example
<div ng-app="" ng-controller="customersController">
<ul>
<ling-repeat="x in names">
{{ x.Name + ', ' + x.Country }}
</li>
18
</ul>
</div>
<script>
function customersController($scope,$http) {
$http.get("http://www.w3schools.com/website/Customers_JSON.php")
.success(function(response) {$scope.names = response;});
}
</script>
Try it Yourself
Application explained:
The AngularJS application is defined by ng-app. The application runs inside a <div>.
The ng-controller directive names the controller object.
The customersController function is a standard JavaScript object constructor.
AngularJS will invoke customersController with a $scope and$http object.
$scope is the application object (the owner of application variables and functions).
$http is an XMLHttpRequest object for requesting external data.
$http.get() reads static JSON data from
http://www.w3schools.com/website/Customers_JSON.php.
If success, the controller creates a property (names) in the scope, with JSON data
from the server.
The code above can also be used to fetch data from a database.
AngularJS Tables
Previous
Next Chapter
The ng-repeat directive is perfect for displaying tables.
AngularJS Example
<div ng-app="" ng-controller="customersController">
<table>
<tr ng-repeat="x in names">
<td>{{ x.Name }}</td>
<td>{{ x.Country }}</td>
</tr>
</table>
</div>
<script>
function customersController($scope,$http) {
$http.get("http://www.w3schools.com/website/Customers_JSON.php")
.success(function(response) {$scope.names = response;});
}
</script>
Try it Yourself
CSS Style
<style>
table, th , td {
border: 1px solid grey;
border-collapse: collapse;
padding: 5px;
}
table tr:nth-child(odd) {
background-color: #f1f1f1;
}
table tr:nth-child(even) {
background-color: #ffffff;
}
</style>
Try it Yourself
AngularJS Example
<table>
<tr ng-repeat="x in names | orderBy : 'Country'">
<td>{{ x.Name }}</td>
<td>{{ x.Country }}</td>
</tr>
</table>
Try it Yourself
AngularJS Example
<table>
<tr ng-repeat="x in names">
<td>{{ x.Name }}</td>
<td>{{ x.Country | uppercase}}</td>
</tr>
</table>
Try it Yourself
AngularJS SQL
Previous
Next Chapter
The code from the previous chapter can also be used to read from databases
21
22
Try it Yourself
23
$outp = "[";
while($rs = $result->fetch_array(MYSQLI_ASSOC)) {
if ($outp != "[") {$outp .= ",";}
$outp .= '{"Name":"' . $rs["CompanyName"] . '",';
$outp .= '"City":"' . $rs["City"] . '",';
$outp .= '"Country":"'. $rs["Country"] . '"}';
}
$outp .="]";
$conn->close();
echo($outp);
?>
24
AngularJS Example
<div ng-app="">
<p>
<button ng-disabled="mySwitch">Click Me!</button>
</p>
<p>
<input type="checkbox" ng-model="mySwitch">Button
</p>
</div>
Try it Yourself
Application explained:
The ng-disabled directive binds the application data mySwitch to the HTML button's
disabled attribute.
26
The ng-model directive binds the value of the HTML checkbox element to the value of
mySwitch.
If the value of mySwitch evaluates to true, the button will be disabled:
<p>
<button disabled>Click Me!</button>
</p>
If the value of mySwitch evaluates to false, the button will not be disabled:
<p>
<button>Click Me!</button>
</p>
AngularJS Example
<div ng-app="">
<p ng-show="true">I am visible.</p>
<p ng-show="false">I am not visible.</p>
</div>
Try it Yourself
The ng-show directive shows (or hides) an HTML element based on the value of ngshow.
You can use any expression that evaluates to true or false:
AngularJS Example
<div ng-app="">
<p ng-show="hour > 12">I am visible.</p>
</div>
Try it Yourself
In the next chapter, there are more examples, using the click of a button to hide
HTML elements.
27
AngularJS Example
<div ng-app="">
<p ng-hide="true">I am not visible.</p>
<p ng-hide="false">I am visible.</p>
</div>
Try it Yourself
AngularJS Events
Previous
Next Chapter
AngularJS has its own HTML events directives.
AngularJS Example
<div ng-app="" ng-controller="myController">
<button ng-click="count = count + 1">Click me!</button>
<p>{{ count }}</p>
</div>
Try it Yourself
28
AngularJS Example
<div ng-app="" ng-controller="personController">
<button ng-click="toggle()">Toggle</button>
<p ng-hide="myVar">
First Name: <input type="text" ng-model="firstName"><br>
Last Name: <input type="text" ng-model="lastName"><br>
<br>
Full Name: {{firstName + " " + lastName}}
</p>
</div>
<script>
function personController($scope) {
$scope.firstName = "John",
$scope.lastName = "Doe"
$scope.myVar = false;
$scope.toggle = function() {
$scope.myVar = !$scope.myVar;
};
}
</script>
Try it Yourself
Application explained:
The first part of the personController is the same as in the chapter about controllers.
The application has a default property (a variable): $scope.myVar = false;
The ng-hide directive sets the visibility, of a <p> element with two input fields,
according to the value (true or false) of myVar.
The function toggle() toggles myVar between true and false.
The value ng-hide="true" makes the element invisible.
29
AngularJS Example
<div ng-app="" ng-controller="personController">
<button ng-click="toggle()">Toggle</button>
<p ng-show="myVar">
First Name: <input type="text" ng-model="firstName"><br>
Last Name: <input type="text" ng-model="lastName"><br>
<br>
Full Name: {{firstName + " " + lastName}}
</p>
</div>
<script>
function personController($scope) {
$scope.firstName = "John",
$scope.lastName = "Doe"
$scope.myVar = true;
$scope.toggle = function() {
$scope.myVar = !$scope.myVar;
};
}
</script>
Try it Yourself
AngularJS Modules
Previous
Next Chapter
Modules define applications. All application controllers should belong to a module.
Modules make your application more readable, and keep the global namespace clean.
30
AngularJS Example
<!DOCTYPE html>
<html>
<head>
<script
src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
</head>
<body>
<div ng-app="myApp"ng-controller="myCtrl">
{{ firstName + " " + lastName }}
</div>
<script src="myApp.js"></script>
<script src="myCtrl.js"></script>
</body>
</html>
Try it Yourself
AngularJS Example
<!DOCTYPE html>
<html>
<head>
<script
src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
</head>
31
<body>
<div ng-app=""ng-controller="myCtrl">
{{ firstName + " " + lastName }}
</div>
<script>
function myCtrl($scope) {
$scope.firstName = "John";
$scope.lastName = "Doe";
}
</script>
</body>
</html>
Try it Yourself
AngularJS Example
<!DOCTYPE html>
<html>
<head>
<script
src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
</head>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
{{ firstName + " " + lastName }}
</div>
<script>
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
$scope.firstName = "John";
$scope.lastName = "Doe";
});
</script>
32
</body>
</html>
Try it Yourself
A common advise for HTML applications, is to place all scripts at the very bottom of the
<body> element.
But, in many AngularJS examples, you will see the library in the < head> element.
This is because calls to angular.module can only be compiled after the library has been
loaded.
Another solution is to load the AngularJS library in the <body> element, but before
your own AngularJS scripts:
AngularJS Example
<!DOCTYPE html>
<html>
<body>
<div ng-app="myApp"ng-controller="myCtrl">
{{ firstName + " " + lastName }}
</div>
<script
src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
<script>
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
$scope.firstName = "John";
$scope.lastName = "Doe";
});
</script>
</body>
</html>
Try it Yourself
33
AngularJS Example
<!DOCTYPE html>
<html>
<head>
<script
src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
</head>
<body>
<div ng-app="myApp"ng-controller="myCtrl">
{{ firstName + " " + lastName }}
</div>
<script src="myApp.js"></script>
<script src="myCtrl.js"></script>
</body>
</html>
Try it Yourself
34
AngularJS Forms
Previous
Next Chapter
HTML Controls
HTML input elements are called HTML controls:
input elements
select elements
button elements
textarea elements
HTML Forms
HTML forms group HTML controls together.
RESET
form = {"firstName":"John","lastName":"Doe"}
master = {"firstName":"John","lastName":"Doe"}
35
Application Code
<div ng-app="" ng-controller="formController">
<formnovalidate>
First Name:<br>
<input type="text" ng-model="user.firstName"><br>
Last Name:<br>
<input type="text" ng-model="user.lastName">
<br><br>
<button ng-click="reset()">RESET</button>
</form>
<p>form = {{user}}</p>
<p>master = {{master}}</p>
</div>
<script>
function formController ($scope) {
$scope.master = {firstName: "John", lastName: "Doe"};
$scope.reset = function() {
$scope.user = angular.copy($scope.master);
};
$scope.reset();
};
</script>
Try it Yourself
Example Explained
The ng-app directive defines the AngularJS application.
The ng-controller directive defines the application controller.
The ng-model directive binds two input elements to the user object in the model.
The formController() function sets initial values to themaster object, and invokes the
reset() method.
36
The reset() method sets the user object equal to the master object.
The ng-click directive invokes the reset() method, only if the button is clicked.
The novalidate attribute is not needed for this application, but normally you will use it in
AngularJS forms, to override standard HTML5 validation.
Input Validation
In the previous chapter, you learned about AngularJS forms and controls.
AngularJS forms and controls can provide validation services, and notify users of invalid
input.
Client-side validation cannot alone secure user input. Server side validation is also
necessary.
Application Code
<!DOCTYPE html>
<html>
<head>
<script
src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
</head>
<body>
<h2>Validation Example</h2>
<form ng-app="" ng-controller="validateCtrl"
name="myForm" novalidate>
<p>Username:<br>
<input type="text" name="user" ng-model="user"required>
<span style="color:red" ng-show="myForm.user.$dirty && myForm.user.$invalid">
<span ng-show="myForm.user.$error.required">Username is required.</span>
37
</span>
</p>
<p>Email:<br>
<input type="email" name="email" ng-model="email" required>
<spanstyle="color:red" ng-show="myForm.email.$dirty && myForm.email.$invalid">
<span ng-show="myForm.email.$error.required">Email is required.</span>
<span ng-show="myForm.email.$error.email">Invalid email address.</span>
</span>
</p>
<p>
<input type="submit"
ng-disabled="myForm.user.$dirty && myForm.user.$invalid ||
myForm.email.$dirty && myForm.email.$invalid">
</p>
</form>
<script>
function validateCtrl($scope) {
$scope.user = 'John Doe';
$scope.email = '[email protected]';
}
</script>
</body>
</html>
Try it Yourself
The HTML form attribute novalidate is used to disable default browser validation.
Example Explained
The AngularJS directive ng-model binds the input elements to the model.
The model object has two properties: user and email.
Because of ng-show, the spans with color:red are displayed only when user or email is
$dirty and $invalid.
Bootstrap is a popular style sheet. This chapter demonstrates how to use it with
AngularJS.
Bootstrap
To include Bootstrap in your AngularJS application, add the following line to your
<head> element:
<link rel="stylesheet"
href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
If you want to study Bootstrap, visit our Bootstrap Tutorial.
Below is a complete HTML example, with all AngularJS directives and Bootstrap classes
explained.
HTML Code
<!DOCTYPE html>
<html ng-app="">
<head>
<link
rel="stylesheet"href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.m
in.css">
<script
src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
</head>
<bodyng-controller="userController">
<div class="container">
<h3>Users</h3>
<tableclass="table table-striped">
<thead><tr>
<th>Edit</th>
<th>First Name</th>
<th>Last Name</th>
</tr></thead>
<tbody><trng-repeat="user in users">
<td>
<button class="btn" ng-click="editUser(user.id)">
<span class="glyphicon glyphicon-pencil"></span> Edit
</button>
</td>
<td>{{ user.fName }}</td>
<td>{{ user.lName }}</td>
39
</tr></tbody>
</table>
<hr>
<button class="btn btn-success"ng-click="editUser('new')">
<span class="glyphicon glyphicon-user"></span> Create New User
</button>
<hr>
<h3 ng-show="edit">Create New User:</h3>
<h3 ng-hide="edit">Edit User:</h3>
<form class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label">First Name:</label>
<divclass="col-sm-10">
<input type="text" ng-model="fName" ng-disabled="!edit"placeholder="First Name">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Last Name:</label>
<divclass="col-sm-10">
<input type="text" ng-model="lName" ng-disabled="!edit"placeholder="Last Name">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Password:</label>
<divclass="col-sm-10">
<input type="password" ng-model="passw1"placeholder="Password">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Repeat:</label>
<divclass="col-sm-10">
<input type="password" ng-model="passw2"placeholder="Repeat Password">
</div>
</div>
</form>
<hr>
<button class="btn btn-success" ng-disabled="error || incomplete">
<span class="glyphicon glyphicon-save"></span> Save Changes
</button>
</div>
<script src = "myUsers.js"></script>
</body>
</html>
Try it Yourself
40
Description
<html ng-app
<button ng-click
<h3 ng-show
<h3 ng-hide
<input ng-model
Bootstrap Class
Defines
<div>
container
A content container
<table>
table
A table
<table>
table-striped
A striped table
<button>
btn
A button
<button>
btn-success
A success button
<span>
glyphicon
A glyph icon
<span>
glyphicon-pencil
A pencil icon
<span>
glyphicon-user
A user icon
<span>
glyphicon-save
A save icon
<form>
form-horizontal
A horizontal form
<div>
form-group
A form group
<label>
control-label
A control label
41
<label>
col-sm-2
A 2 columns span
<div>
col-sm-10
A 10 columns span
JavaScript Code
function userController($scope) {
$scope.fName = '';
$scope.lName = '';
$scope.passw1 = '';
$scope.passw2 = '';
$scope.users = [
{id:1, fName:'Hege', lName:"Pege" },
{id:2, fName:'Kim', lName:"Pim" },
{id:3, fName:'Sal', lName:"Smith" },
{id:4, fName:'Jack', lName:"Jones" },
{id:5, fName:'John', lName:"Doe" },
{id:6, fName:'Peter', lName:"Pan" }
];
$scope.edit = true;
$scope.error = false;
$scope.incomplete = false;
$scope.editUser = function(id) {
if (id == 'new') {
$scope.edit = true;
$scope.incomplete = true;
$scope.fName = '';
$scope.lName = '';
} else {
$scope.edit = false;
$scope.fName = $scope.users[id-1].fName;
$scope.lName = $scope.users[id-1].lName;
}
};
$scope.$watch('passw1',function() {$scope.test();});
$scope.$watch('passw2',function() {$scope.test();});
$scope.$watch('fName', function() {$scope.test();});
$scope.$watch('lName', function() {$scope.test();});
$scope.test = function() {
if ($scope.passw1 !== $scope.passw2) {
$scope.error = true;
} else {
$scope.error = false;
}
$scope.incomplete = false;
if ($scope.edit && (!$scope.fName.length ||
42
!$scope.lName.length ||
!$scope.passw1.length || !$scope.passw2.length)) {
$scope.incomplete = true;
}
};
}
Used for
$scope.fName
$scope.lName
$scope.passw1
$scope.passw2
$scope.users
$scope.edit
$scope.error
$scope.incomplete
$scope.editUser
$scope.watch
$scope.test
AngularJS Includes
Previous
Next Chapter
With AngularJS, you can include HTML files in HTML.
43
PHP Example
<?php require("navigation.php"); ?>
Example
<body>
<divclass="container">
<div ng-include="'myUsers_List.htm'"></div>
<divng-include="'myUsers_Form.htm'"></div>
</div>
</body>
Try it Yourself
myUsers_List.html
<table class="table table-striped">
<thead><tr>
<th>Edit</th>
<th>First Name</th>
<th>Last Name</th>
</tr></thead>
<tbody><trng-repeat="user in users">
<td>
<button class="btn" ng-click="editUser(user.id)">
<span class="glyphicon glyphicon-pencil"></span> Edit
</button>
</td>
<td>{{ user.fName }}</td>
<td>{{ user.lName }}</td>
</tr></tbody>
</table>
Try it Yourself
45
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Repeat:</label>
<div class="col-sm-10">
<input type="password" ng-model="passw2" placeholder="Repeat Password">
</div>
</div>
</form>
<hr>
<button class="btn btn-success"ng-disabled="error || incomplete">
<span class="glyphicon glyphicon-save"></span> Save Changes
</button>
Try it Yourself
AngularJS Application
Previous
Next Chapter
It is time to create a real AngularJS single page application (SPA).
46
My Note
SaveClear
Number of characters left: 100
Application Explained
AngularJS Example
<html ng-app="myNoteApp">
<head>
<script
src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
</head>
<body>
<div ng-controller="myNoteCtrl">
<h2>My Note</h2>
<p><textarea ng-model="message" cols="40" rows="10"></textarea></p>
<p>
<button ng_click="save()">Save</button>
<button ng-click="clear()">Clear</button>
</p>
47
48
<script src="myNoteApp.js"></script>
<script src="myNoteCtrl.js"></script>
END OF W3SCHOOLS\\\\
49
Table of Contents
Here is a simple "hello world" example made with AngularJS which shows the model,
view and controller parts of an AngularJS app:
<!DOCTYPEhtml>
<htmllang="en">
<head>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js"></scrip
t>
</head>
<bodyngapp="myapp">
<divngcontroller="HelloController">
<h2>Hello{{helloTo.title}}!</h2>
</div>
<script>
angular.module("myapp",[])
.controller("HelloController",function($scope){
51
$scope.helloTo={};
$scope.helloTo.title="World,AngularJS";
});
</script>
</body>
</html>
<h2>Hello{{helloTo.title}}!</h2>
</div>
Notice the ngcontroller attribute. This attribute tells AngularJS what controller to use
with this view. Notice also the {{helloTo.title}} text. This is part of AngularJS's
template system. This tells AngularJS to write the "model" value
named helloTo.title to the HTML at this location.
The "controller" is this part:
<script>
angular.module("myapp",[])
52
.controller("HelloController",function($scope){
$scope.helloTo={};
$scope.helloTo.title="World,AngularJS";
});
</script>
This code registers a controller function named "HelloController" in the angular module
named "myapp". Angular modules will be explained later in this tutorial.
The $scope parameter passed to the controller function is the "model". The controller
function adds a helloToJavaScript object, and in that object it adds a title field. It is
this helloTo.title value from the model which the view writes into the HTML.
<body>
</body>
</html>
53
Second, we need to include the AngularJS JavaScript file in the HTML page so we can
use AngularJS:
<!DOCTYPEhtml>
<html>
<head>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js"></scrip
t>
</head>
<body>
</body>
</html>
Remember to check AngularJS's website for the latest version of AngularJS so you do
not keep using the version I use in this example.
Third, we need to tell AngularJS what part of our HTML page contains the AngularJS
app. You do so by adding thengapp attribute to the root HTML element of the
AngularJS app. Typically, the root element is either the htmlelement or
the body element. In this example I insert the ngapp attribute into the body element:
<!DOCTYPEhtml>
<html>
<head>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js"></scrip
t>
</head>
54
<bodyngapp="myapp">
</body>
</html>
In this example I set the ngapp value to myapp. This means that all controller functions
must be added to the myappmodule. In other words, the AngularJS module
named myapp contains all the controllers for this AngularJS application. The
name myapp is something I have chosen. You can choose your module names freely.
Fourth, we need a view. A view is a section of HTML which is enclosed in an HTML
element. Here is an example:
<!DOCTYPEhtml>
<html>
<head>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js"></scrip
t>
</head>
<bodyngapp="myapp">
<divngcontroller="HelloController">
<h2>Hello{{helloTo.title}}!</h2>
</div>
</body>
</html>
55
In this example the view is the div element and everything inside it. The div element
contains ngcontrollerattribute with the value HelloController. This means, that the
controller function used by this view is namedHelloController.
The view div element contains the HTML
<h2>Hello{{helloTo.title}}!</h2>
This is standard HTML except for the {{helloTo.title}} part. This part tells AngularJS
to insert the model value named helloTo.title into the HTML at that location.
Fifth, we need a controller function. A controller function is a normal JavaScript function
which takes a single parameter: The $scope parameter. The $scope parameter is the
model object to be used by the controller function and the corresponding view. The
controller function can insert data and functions into the model object. The view can
then use the data and the functions.
Here is our little web app with the controller function inserted:
<!DOCTYPEhtml>
<html>
<head>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js"></scrip
t>
</head>
<bodyngapp="myapp">
<divngcontroller="HelloController">
<h2>Hello{{helloTo.title}}!</h2>
56
</div>
<script>
angular.module("myapp",[])
.controller("HelloController",function($scope){
$scope.helloTo={};
$scope.helloTo.title="World,AngularJS";
});
</script>
</body>
</html>
Interpolation Directive
ng-bind Directive
Conditional Rendering
ng-switch Directive
ng-if Directive
ng-include Directive
ng-repeat Directive
Filtering
o
Formatting Filters
Array Filters
Chaining Filters
59
In the first text of this tutorial you saw how AngularJS splits an application into views,
controllers and models (MVC). This text will dive deeper into how to create AngularJS
views.
Before we start, let me first set up a simple AngularJS application which you can use to
play around with the examples in this text:
<!DOCTYPEhtml>
<html>
<head>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js"></scrip
t>
</head>
<bodyngapp="myapp">
<divngcontroller="MyController">
<span></span>
</div>
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
//emptycontrollerfunction
});
</script>
60
</body>
</html>
AngularJS Directives
AngularJS views mix data from the model into an HTML template. You use
AngularJS directives to tell AnguluarJS how to mix the data into the HTML template.
This text will cover the most commonly used AngularJS directives.
Interpolation Directive
The interpolation directive is one of the most fundamental directives in AngujarJS. The
interpolation directive inserts the result of an expression into the HTML template. You
mark where to insert the expression using the {{}} notation. Here is an example:
<divngcontroller="MyController">
<span>{{myData.text}}</span>
</div>
The HTML template is contained within the div element with the ng
controller attribute. Inside the HTML template is a span element, and inside this is an
interpolation directive. This directive instructs AngularJS to insert the data
value myData.text at the given location.
The interpolation directive can also insert data returned from functions of the model
object. Here is an example:
<divngcontroller="MyController">
<span>{{myData.textf()}}</span>
</div>
61
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
$scope.myData={};
$scope.myData.textf=function(){return"Atextfromafunction";};
});
</script>
ng-bind Directive
The ngbind directive is an alternative to the interpolation directive. You use it by
inserting an ngbind attribute into the HTML element you want AngularJS to insert data
into. Here is an example:
<divngcontroller="MyController">
<spanngbind="myData.textf()"></span>
</div>
This will insert the data returned from the myData.text() function into the body of
the span element. Notice how the{{}} are not necessary around the expression inside
the ngbind attribute.
If the data obtained from the model contains HTML elements, these are escaped before
being inserted into the HTML template. The escaping means that the HTML is displayed
as text, and not as HTML.
This is done to prevent HTML injection attacks. For instance, in a chat application
somebody might insert a <script>element with JavaScript into a chat message. If this
element was not escaped, anyone seeing the chat message might have
the <script> element executed. With the HTML escaping the <script> element will just
be displayed as text.
You can disable the HTML escaping by using the ngbindhtmlunsafe directive, like
this:
<divngcontroller="MyController">
<spanngbindhtmlunsafe="myData.textf()"></span>
</div>
You should be really careful when disabling HTML escaping. Make sure that no HTML is
displayed which is not trusted.
Conditional Rendering
AngularJS can show or hide HTML depending on the state of data in the model. You do
so using a set of AngularJS directives which are created specifically for that purpose. I
will cover these directives in the following sections.
63
<spannghide="myData.showIt"></span>
</div>
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
$scope.myData={};
$scope.myData.showIt=true;
});
</script>
This example creates two span elements. One has an ngshow directive and the other
has an nghide directive. Both directives look at the myData.showIt boolean variable to
determine if they should show or hide the spanelement. The ngshow directive will show
the element if the model value is true, and hide the element if the model value is false.
The nghide directive will do the opposite: Hide the span element if the model value is
true, and show it if the model value is false.
Notice how the controller function sets the myData.showIt to true. This means that the
example above will show the first span element and hide the second.
The HTML elements (span elements in this case) are hidden using the CSS
property display:none;. That means, that the HTML elements are still present in the
DOM. They are just not visible.
ng-switch Directive
The ngswitch directive is used if you want to add or remove HTML elements from the
DOM based on data in the model. Here is an example:
<divngcontroller="MyController">
64
<divngswitchon="myData.switch">
<divngswitchwhen="1">Shownwhenswitchis1</div>
<divngswitchwhen="2">Shownwhenswitchis2</div>
<divngswitchdefault>Shownwhenswitchisanythingelsethan1and
2</div>
</div>
</div>
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
$scope.myData={};
$scope.myData.switch=3;
});
</script>
This example contains a div element with an ngswitch attribute and an on attribute.
The on attribute tells which data in the model to switch on.
Inside the div element are three nested div elements. The first two nested div elements
contains an ngswitchwhen attribute. The value of this attribute tells what value the
model data referenced in the on attribute of the parentdiv should have, for the
nested div to be visible. In this example the first nested div is visible
whenmyData.switch is 1, and the second nested div is visible when myData.switch is 2.
The third nested div has an ngswitchdefault attribute. If no of the other ngswitch
when directives are matched, then the div with the ngswitchdefault attribute is
shown.
65
In the example above the controller function sets myData.switch to 3. That means that
the nested div with the ngswitchdefault attribute will be shown. The two other
nested div elements will be removed from the DOM completely.
ng-if Directive
The ngif directive can include / remove HTML elements from the DOM, just like
the ngswitch directive, but it has a simpler syntax. Here is an example:
<divngcontroller="MyController">
<divngif="myData.showIt">ngifShowit</div>
</div>
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
$scope.myData={};
$scope.myData.showIt=true;
});
</script>
The main difference between ngif and ngshow + nghide is that ngif removes the
HTML element completely from the DOM, whereas the ngshow + nghide just applies
the CSS property display:none; to the elements.
ng-include Directive
The nginclude directive can be used to include HTML fragments from other files into
the view's HTML template. Here is an example:
66
<divngcontroller="MyController">
<divnginclude="'angularincludedfragment.html'"></div>
</div>
This example includes the file angularincludedfragment.html into the HTML template
inside the div having the nginclude attribute. Notice how the file name is quoted
(single quotes).
You can include HTML fragments based on conditions. For instance, you can choose
between two files like this:
<divngcontroller="MyController">
<divnginclude="myData.showIt&&
'fragment1.html'||
'fragment2.html'"></div>
</div>
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
$scope.myData={};
$scope.myData.showIt=true;
});
</script>
67
ng-repeat Directive
The ngrepeat directive is used to iterate over a collection of items and generate HTML
from it. After the initial generation the ngrepeat monitors the items used to generate the
HTML for changes. If an item changes, the ngrepeat directive may update the HTML
accordingly. This includes reordering and removing DOM nodes.
Here is a simple ngrepeat example:
<ol>
<lingrepeat="theIteminmyData.items">{{theItem.text}}</li>
</ol>
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
$scope.myData={};
$scope.myData.items=[{text:"one"},{text:"two"},{text:
"three"}];
});
</script>
This example will create an li element for each item in the myData.items array.
You can also iterate over collections returned from a function call. Here is an example:
<ol>
<lingrepeat="theIteminmyData.getItems()">{{theItem.text}}</li>
</ol>
68
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
$scope.myData={};
$scope.myData.items=[{text:"one"},{text:"two"},{text:
"three"}];
$scope.myData.getItems=function(){returnthis.items;};
});
</script>
And you can iterate over the properties of a JavaScript object using a slightly different
syntax:
<ol>
<lingrepeat="(name,value)inmyData.myObject">{{name}}={{value}}</li>
</ol>
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
$scope.myData={};
$scope.myData.myObject={var1:"val1",var2:"val3",var3:"val3"};
});
</script>
69
Notice the (name,value) part of the ngrepeat directive. That signals to AngularJS to
iterate over the properties of an object. The name parameter will be bound to the
property name, and the value parameter will be bound to the property value.
The name and value parameters can be output to the HTML template just like any other
JavaScript variable or object property, as you can see from the HTML template above.
$index
$first
$middle
$last
The $index variable contains the index of the element being iterated.
The $first, $middle and $last contain a boolean value depending on whether the
current item is the first, middle or last element in the collection being iterated. An item is
"middle" if it is not first nor last. You can use these variables to generate different HTML
using e.g. the ngshow / nghide, ngswitch, ngif and nginclude directives described
earlier.
70
</div>
Wrapping the element to be repeated in a root element may not always be possible
though. Therefore AngularJS has the ngrepeatstart and ngrepeatend directives
which mark which element to start and end the repetition with. Here is an example:
<ol>
<lingrepeatstart="(name,value)inmyData.myObject">{{name}}</li>
<lingrepeatend>{{value}}</li>
</ol>
This example will repeat both of the li elements for each property in myData.myObject.
Filtering
Some of the directives covered above support filtering. This section will explain how
filtering works.
The ngrepeat directive can accept a filter like this:
<divngrepeat="iteminmyData.items|filter:itemFilter"></div>
Notice the |filter:itemFilter part of the declaration above. That part is the filter
definition. The |filter:part tells AngularJS to apply a filter to the myData.items array.
The itemFilter is the name of the filter function. This function has to be present on
the $scope object, and it has to return either true or false. If the filter function returns
true, then the element from the array is used by the ngrepeat directive. If the filter
function returns false, the element is ignored. Here is an example:
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
71
$scope.myData={};
$scope.myData.items=[{text:"one"},{text:"two"},{text:"three"},
{text:"four"}];
$scope.itemFilter=function(item){
if(item.text=="two")returnfalse;
returntrue;
}
}
});
</script>
Formatting Filters
AngularJS comes with a set of built-in formatting filters which can be used in conjunction
with the interpolation directive, and with ngbind. Here is a list of the formatting filters:
Filter
Description
date
currency
72
number
json
This example shows the date filter which can format a JavaScript date object according
to the date format pattern given after the |date:part. It is the myData.theDate property
that will be formatted as a date. Thus, it has to be a JavaScript date object.
Here is a number filter example:
<span>{{myData.theNumber|number:2}}</span>
73
Array Filters
AngularJS also contains a set of array filters which filters or transforms arrays. These
filters are:
Array Filters:
Filter
Description
limitTo Limits the array to the given size, beginning from some index in the array.
The limitTo filter also works on strings.
filter
This limits the $scope myData.theText variable to a length of 3 characters. If this filter
had been applied to an array, the array would have been limited to 3 elements.
The filter filter is a special filter which can do a lot of different things. In its simplest
form it simply calls a function on the $scope object. This function must
return true or false. True is returned if the filter accepts the value passed to it. False is
returned if the filter cannot accept the value. If the filter cannot accept the value, the
value is not included in the array resulting from the filtering. Here is an example:
74
<ol>
<lingrepeat="iteminmyData.items|filter:filterArray">
{{item.text}}:{{$first}},{{$middle}},{{$last}}
</li>
</ol>
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
$scope.myData={};
$scope.myData.items=
[{text:"one"},{text:"two"},{text:"three"},{text:
"four"}];
$scope.filterArray=function(item){
if(item.text=="two")returnfalse;
returntrue;
}
});
</script>
This example calls the filterArray() function which filters out items which has
a text property with the value two.
Here is an orderBy example:
<ol>
<lingrepeat="iteminmyData.items|orderBy:sortField:reverse">
75
{{item.text}}:{{$first}},{{$middle}},{{$last}}
</li>
</ol>
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
$scope.myData={};
$scope.myData.items=[{text:"one"},{text:"two"},{text:
"three"},{text:"four"}];
$scope.sortField="text";
$scope.reverse=true;
});
</script>
The orderBy filter takes a $scope variable as parameter. In this example that variable is
named sortField. The value of this variable is the name of the property on the sorted
data objects which is used to sort the data objects. In this example
the sortField property is set to text which means that the data object's text property is
used to sort the data objects.
The orderBy filter can also take a second $scope variable as parameter. In this example
that variable is namedreverse. The value of this variable determines if the data objects
are to be sorted in their natural order, or the reverse order of that. In this case
the reverse variable is set to true, meaning the data objects will be sorted in reverse
order.
76
Chaining Filters
It is possible to chain filters by simply putting more filters after each other in the filter
section. When chaining filters, the output of one filter is used as input for the next filter in
the chain. Here is an example:
<span>{{myData.theText|limitTo:5|uppercase}}</span>
This example assigns the output of the filtering to the filteredItems variable. The
example then refers to this variable inside the {{}} directive under the ol element.
77
<div>Filtered:{{myData.text|myFilter}}</div>
<script>
varmodule=angular.module("myapp",[]);
module.filter('myFilter',function(){
returnfunction(stringValue){
returnstringValue.substring(0,3);
};
});
</script>
This example registers a filter with AngularJS which can filter strings. The filter returns
the first 3 characters of the string. The filter is registered with the name myFilter. It is
this name you will have to use when referencing the filter, as you can see in the
beginning of the filter.
If your filter needs more input parameters, add more parameters to the filter function,
and add the parameters after the filter name and a : when referencing it. Here is an
example:
<div>Filtered:{{myData.text|myFilter:2:5}}</div>
<script>
varmodule=angular.module("myapp",[]);
78
module.filter('myFilter',function(){
returnfunction(stringValue,startIndex,endIndex){
returnstringValue.substring(parseInt(startIndex),
parseInt(endIndex));
};
});
</script>
Notice how the filter reference (|myfilter:2:5) now has two values after the filter
name, each value separated by a colon. These two values are passed to the filter as
parameters. Notice also how the filter function now takes two extra parameters
named startIndex and endIndex. These two parameters are used to determine which
part of the string to return as substring from the filter.
<head>
79
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js"></scrip
t>
</head>
<bodyngapp="myapp">
<divngcontroller="myController1">
{{data.theVar}}
</div>
<divngcontroller="myController2">
{{data.theVar}}
</div>
<script>
varmodule=angular.module("myapp",[]);
varmyController1=module.controller("myController1",function($scope){
$scope.data={theVar:"ValueOne"};
});
80
varmyController2=module.controller("myController2",function($scope){
$scope.data={theVar:"ValueTwo"};
});
</script>
</body>
</html>
This example contains two views, each with their own controller function. Each controller
sets the variabledata.theVar to different values.
When this example is executed, the $scope hierarchy will look like this:
Root $scope
o $scope for myController 1
o
As you can see, the $scope object used by the two controllers are not the
same $scope object. That is also why the example above would write out two different
values for the data bindings {{data.theVar}} inside the two views. The two controller
functions for the views set different values for the data.theVar variable in each their
own $scopeobject.
<head>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js"></scrip
t>
</head>
<bodyngapp="myapp">
<divngcontroller="MyController">
</div>
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
82
$scope.myData={};
});
</script>
</body>
</html>
ngclick
ngdblclick
ngmousedown
ngmouseup
ngmouseenter
ngmouseleave
ngmousemove
ngmouseover
ngkeydown
ngkeyup
ngkeypress
ngchange
<divngcontroller="MyController">
<divngclick="myData.doClick()">Clickhere</div>
</div>
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
$scope.myData={};
$scope.myData.doClick=function(){
alert("clicked");
}
});
</script>
When you click the inner div of the above example, the myData.doClick() function will
get called. As you can see in the controller function, the myData object has
a doClick() function added to it.
As you can see, attaching events to HTML DOM elements is not much different from
generating the HTML in the first place. The event listener functions called are functions
added to the $scope object by the controller function.
The event listener directives have a special variable named $event which you can use
as parameter to the event listener function. The $event variable contains the original
browser event object. Here is an example:
<divngcontroller="MyController">
<divngclick="myData.doClick($event)">Clickhere</div>
</div>
84
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
$scope.myData={};
$scope.myData.doClick=function(event){
alert("clicked:"+event.clientX+","+event.clientY);
}
});
</script>
You can also pass other parameters to your event listener functions. Here is an example
that adds event listener function to a list of li elements:
<divngcontroller="MyController">
<ul>
<lingrepeat="iteminmyData.items"
ngclick="myData.doClick(item,$event)">Clickhere</li>
</ul>
</div>
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
$scope.myData={};
85
$scope.myData.items=[{v:"1"},{v:"2"},{v:"3"}];
$scope.myData.doClick=function(item,event){
alert("clicked:"+item.v+"@"+event.clientX+":"+
event.clientY);
}
});
</script>
This example iterates through a list of items, generates li elements from them, and
adds click listeners to each lielement. Along with the call to the click listener is passed
the item JavaScript object the li element was generated based on, along with the click
event object ($event).
$watch()
$digest()
$apply()
Example
The AngularJS $scope functions $watch(), $digest() and $apply() are some of the
central functions in AngularJS. Understanding $watch(), $digest() and $apply() is
essential in order to understand AngularJS.
86
When you create a data binding from somewhere in your view to a variable on
the $scope object, AngularJS creates a "watch" internally. A watch means that
AngularJS watches changes in the variable on the $scope object. The framework is
"watching" the variable. Watches are created using the $scope.$watch() function which
I will cover later in this text.
At key points in your application AngularJS calls the $scope.$digest() function. This
function iterates through all watches and checks if any of the watched variables have
changed. If a watched variable has changed, a corresponding listener function is called.
The listener function does whatever work it needs to do, for instance changing an HTML
text to reflect the new value of the watched variable. Thus, the $digest() function is
what triggers the data binding to update.
Most of the time AngularJS will call the $scope.$watch() and $scope.
$digest() functions for you, but in some situations you may have to call them yourself.
Therefore it is really good to know how they work.
The $scope.$apply() function is used to execute some code, and then call $scope.
$digest() after that, so all watches are checked and the corresponding watch listener
functions are called. The $apply() function is useful when integrating AngularJS with
other code.
I will get into more detail about the $watch(), $digest() and $apply() functions in the
remainder of this text.
$watch()
The $scope.watch() function creates a watch of some variable. When you register a
watch you pass two functions as parameters to the $watch() function:
A value function
A listener function
Here is an example:
$scope.$watch(function(){},
function(){}
87
);
The first function is the value function and the second function is the listener function.
The value function should return the value which is being watched. AngularJS can then
check the value returned against the value the watch function returned the last time.
That way AngularJS can determine if the value has changed. Here is an example:
$scope.$watch(function(scope){returnscope.data.myVar},
function(){}
);
This example valule function returns the $scope variable scope.data.myVar. If the value
of this variable changes, a different value will be returned, and AngularJS will call the
listener function.
Notice how the value function takes the scope as parameter (without the $ in the name).
Via this parameter the value function can access the $scope and its variables. The value
function can also watch global variables instead if you need that, but most often you will
watch a $scope variable.
The listener function should do whatever it needs to do if the value has changed.
Perhaps you need to change the content of another variable, or set the content of an
HTML element or something. Here is an example:
$scope.$watch(function(scope){returnscope.data.myVar},
function(newValue,oldValue){
document.getElementById("").innerHTML=
""+newValue+"";
}
);
88
This example sets the inner HTML of an HTML element to the new value of the variable,
embedded in the b element which makes the value bold. Of course you could have done
this using the code {{data.myVar}, but this is just an example of what you can do
inside the listener function.
$digest()
The $scope.$digest() function iterates through all the watches in the $scope object,
and its child $scope objects (if it has any). When $digest() iterates over the watches, it
calls the value function for each watch. If the value returned by the value function is
different than the value it returned the last time it was called, the listener function for that
watch is called.
The $digest() function is called whenever AngularJS thinks it is necessary. For
instance, after a button click handler has been executed, or after an AJAX call returns
(after the done() / fail() callback function has been executed).
You may encounter some corner cases where AngularJS does not call
the $digest() function for you. You will usually detect that by noticing that the data
bindings do not upate the displayed values. In that case, call $scope.$digest() and it
should work. Or, you can perhaps use $scope.$apply() instead which I will explain in
the next section.
$apply()
The $scope.$apply() function takes a function as parameter which is executed, and
after that $scope.$digest()is called internally. That makes it easier for you to make sure
that all watches are checked, and thus all data bindings refreshed. Here is
an $apply() example:
$scope.$apply(function(){
$scope.data.myVar="Anothervalue";
});
89
The function passed to the $apply() function as parameter will change the value
of $scope.data.myVar. When the function exits AngularJS will call the $scope.
$digest() function so all watches are checked for changes in the watched values.
Example
To illustrate how $watch(), $digest() and $apply() works, look at this example:
<divngcontroller="myController">
{{data.time}}
<br/>
<buttonngclick="updateTime()">updatetimengclick</button>
<buttonid="updateTimeButton">updatetime</button>
</div>
<script>
varmodule=angular.module("myapp",[]);
varmyController1=module.controller("myController",function($scope){
$scope.data={time:newDate()};
$scope.updateTime=function(){
$scope.data.time=newDate();
}
90
document.getElementById("updateTimeButton")
.addEventListener('click',function(){
console.log("updatetimeclicked");
$scope.data.time=newDate();
});
});
</script>
91
$scope.data.time=newDate();
$scope.$digest();
});
Instead of calling $digest() inside the button listener function you could also have used
the $apply() function like this:
document.getElementById("updateTimeButton")
.addEventListener('click',function(){
$scope.$apply(function(){
console.log("updatetimeclicked");
$scope.data.time=newDate();
});
});
Notice how the $scope.$apply() function is called from inside the button event listener,
and how the update of the$scope.data.time variable is performed inside the function
passed as parameter to the $apply() function. When the $apply() function call finishes
AngularJS calls $digest() internally, so all data bindings are updated.
$http Functions
92
$http as a Function
JSONP Security
Note: So far only the $http service is covered (both normal AJAX and JSONP), but that
is enough to get you started using AJAX in AngularJS. The REST API is not necessary
to understand to start using AJAX (and personally I am not that big a fan of it - you can
do the same just as easily with the $http service).
93
<head>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js"></scrip
t>
</head>
<bodyngapp="myapp">
<divngcontroller="MyController">
<buttonngclick="myData.doClick(item,$event)">SendAJAXRequest</button>
<br/>
Datafromserver:{{myData.fromServer}}
</div>
<script>
angular.module("myapp",[])
.controller("MyController",function($scope,$http){
$scope.myData={};
$scope.myData.doClick=function(item,event){
varresponsePromise=$http.get("/angularjsexamples/jsontest
data.jsp");
responsePromise.success(function(data,status,headers,config){
$scope.myData.fromServer=data.title;
});
94
responsePromise.error(function(data,status,headers,config){
alert("AJAXfailed!");
});
}
});
</script>
</body>
</html>
Notice how the controller function registered with the module takes two parameters:
A $scope object (as always) and an extra $http object. The $http object (or "service") is
used to make AJAX calls.
The the $http.get() function returns a "promise" object. This promise object has
a success() and an error()function. By calling these functions and pass a function to
them as parameter you can control what happens when the AJAX call finishes. If the
AJAX call succeeds (the server sends back an HTTP code between 200 and 209), the
function passed to the success() function is executed. If the AJAX call fails (all other
codes except for redirects), the function passed to the error() method is executed.
The succss() and error() functions are covered in more detail later.
$http Functions
The $http service has several functions you can use to send AJAX requests. These are:
$http.get(url,config)
$http.post(url,data,config)
95
$http.put(url,data,config)
$http.delete(url,config)
$http.head(url,config)
Notice that the $http.post() and $http.put() functions take a data parameter which
contains data to be sent to the server. The rest of the $http functions cannot take a data
parameter.
The data parameter will be converted to a JSON string. This string will be be included in
the request body when the HTTP request is sent to the server. AngularJS will consider
all properties starting with a $ as private, and thus exclude these from the string. If you
need to include properties starting with a $ in the data string, convert the data object to
a string yourself using JSON.stringify(data).
$http as a Function
You can also use the $http service as a function directly, like this:
varpromise=$http(config);
In this case the URL and HTTP method are also set inside the config object.
The config object is explained in the following section.
method
url
params
headers
timeout
96
cache
transformRequest
transformResponse
The method property can be used to set the HTTP method for pthe request. The method
is one of either GET,POST,PUT,DELETE or HEAD. This property is normally set implicitly
via the function you choose to call on the $httpservice, so you will rarely need to set this
property in practice.
The url property can be used to set the URL of the AJAX call. This is already provided
to the various $http functions, so you will rarely need to set this again in
the config object.
The params property is used to set any additional request parameters to be appended to
the URL query string. Theparams property is a JavaScript object with one property per
request parameter to add.
The headers property is used to set any additional HTTP headers you want sent to the
server. The headers property is a JavaScript object with one property per header.
The timeout property is used to set the timeout for the AJAX call. When the timeout limit
is reached, the AJAX call is aborted. The timeout is specified in milliseconds.
The cache property is used to enable XHR GET request caching.
The transformRequest property is used to set a function which can transform the
request object before it is sent to the server.
The transformResponse property is used to set a function which can transform the
response sent back from the server, before it is passed to your application.
Inside the success() and error() function you should set the appropriate values on
the $scope object. That is the way to get the data or error message out to the user.
Update the $scope object with data, and AngularJS will trigger the HTML template
rendering so the data can be made visible to the user.
Both functions take the following parameters:
data
status
headers
config
The data parameter is the JSON object returned by the server. The $http service
assumes that your server sends back JSON.
The status parameter is the HTTP status code returned by the server along with the
response.
The headers parameter is a function that can be used to obtain any HTTP response
headers returned along with the response. You get a header by
calling headers([headerName]);. As you can see, the headers() function take an array
of header names as parameter. The AngularJS documentation is a bit vague about what
the function returns, but I suspect it returns a JavaScript object with one key, value pair
for each header, with the header name as the key (property name).
The config parameter is the configuration object that was used to create the given
HTTP request (AJAX call). In other words, the config object which was passed as
parameter to the $http ajax function call that created this AJAX call, and thereby this
promise object.
98
JSONP is short for "JSON with Padding" (I explain why, later). A JSONP request is not
sent via the XHR object like AJAX calls normally are. Instead, a <script> element is
created and inserted into the HTML page. Here is an example of how such
a <script> element could look:
<scriptsrc="http://jenkov.com/theService.json?
callback=theServiceResponse&p1=v1&p2=v2"></script>
The src attribute contains the URL of the remote service to call with the JSONP call.
This URL should include any parameters you need to send to the remote service.
When the <script> element is inserted into the HTML page, the browser will load the
script from the given URL. That enables the remote service to send JavaScript back to
your application for execution.
The JavaScript returned by the remote service should be a function call to an existing
JavaScript function in your HTML page. Here is how the returned JavaScript could look:
theServiceResponse({name:"John",title:"CEO",company:"BigFatCo"});
This code makes a function call to the function named theServiceResponse. This
function must be present in your HTML page already. Inside this function you process
the response sent back from the service.
When the theServiceResponse() function is called, a JavaScript object is passed to the
function as parameter. This JavaScript object contains the response parameters from
the service call. Thus, this JavaScript object is generated by the remote service.
Whatever the remote service wants to send back to you is what this JavaScript object
contains. Seen from the remote service's perspective, that JavaScript object is just
JSON like any other JSON sent back to an HTML page. The difference is that this JSON
object is wrapped in a function call. This function call is the "padding" part of the "JSON
with Padding" name for JSONP.
You might wonder how the remote service knows the name of the function to wrap the
returned JSON in. The answer is, that you pass that name to the remote service along
with the other request parameters. The function name is one of the request parameters.
99
By default this parameter name is callback but you will have to check with the concrete
service to see what parameter name it expects for the function name.
In AngularJS the function name is supplied behind the scene by AngularJS, so you don't
have to worry about adding it to the URL of the remote service.
You use JSONP calls via the $http service like this:
$http.jsonp(url,config);
Like with the AJAX functions the jsonp() function takes a url and a config object. Here
is an example of a JSONP call with the url and config objects supplied:
varurl=http://jenkov.com/theService.json?callback=JSON_CALLBACK";
varresponsePromise=$http.jsonp(url,
{params:{
p1:"v1"
,p2:"v2"
}
}
);
responsePromise.success(function(data){
//dosomethingwiththereturnedJavaScriptobject
//(inthe"data"parameter).
});
the $http service, the config object can contain a params field. This field should be a
JavaScript object containing all the request parameters to append to the URL of the
remote service.
When you call the $http.jsonp() function AngularJS will create <script> element for
you, and insert it into your HTML page. AngularJS will also create the final URL of the
remote service by appending the parameters passed in the config.params object to the
URL.
The URL passed to the $http.jsonp() function must contain
the callback=JSON_CALLBACK parameter. AngularJS will replace the JSONCALLBACK string
with the name of a callback function which AngularJS creates.
The promise object returned by the $http.jsonp() function has a success() function,
just like the other AJAX function call promise objects. Unfortunately we do not have
access to the HTTP headers of the response sent back from the server, since that
request is handled by the browser internally, when evaluating the <script
src="url">element. That also means that if the JSONP call fails, we have no way of
knowing so, since no callback function will get called in that case (no JavaScript to
execute from the remote service).
JSONP Security
You have to be careful with JSONP calls. When you make a JSONP call. The remote
service could send back anyJavaScript which would then get executed inside your
HTML page. An evil remote service could send back JavaScript which attempts to steal
information from your application and sent it to a third party service. Only make JSONP
calls to services you trust.
Binding Checkboxes
ng-options
Empty Options
Form Validation
ng-minlength + ng-maxlength
ng-pattern
ng-required
Submitting Forms
This binding is two-way, meaning if the $scope.myForm.firstName has a value set inside
the corresponding controller function, the input field will start with that value.
Additionally, once the user types something into the text field, that value will be copied
from the text field into the $scope.myForm.firstName property.
Here is a full AngularJS form example you can play with:
<!DOCTYPEhtml>
<html>
102
<head>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js"></scrip
t>
</head>
<bodyngapp="myapp">
<divngcontroller="MyController">
<form>
<inputtype="text"name="firstName"ngmodel="myForm.firstName">First
name<br/>
<inputtype="text"name="lastName"ngmodel="myForm.lastName">Lastname
<br/>
</form>
<div>
{{myForm.firstName}}{{myForm.lastName}}
</div>
</div>
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
$scope.myForm={};
$scope.myForm.firstName="Jakob";
103
$scope.myForm.lastName="Jenkov";
});
</script>
</body>
</html>
Binding Checkboxes
If you bind a checkbox (<inputtype="checkbox">) to a model property, the model
property will be set to true if the checkbox is checked, and false if not.
If you need other values instead of true and false inserted into your model, you can
use the ngtruevalue andngfalsevalue directives, like this:
<inputtype="checkbox"ngmodel="myForm.wantNewsletter"
ngtruevalue="yes"ngfalsevalue="no">
104
<div>
{{myForm.car}}
</div>
</div>
105
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
$scope.myForm={};
$scope.myForm.car="nissan";
});
</script>
ng-options
Instead of using static HTML options you can have AngularJS create option elements
based on data from the $scopeobject. You do so using the ngoptions directive inside
the select element. Here is an example:
<divngcontroller="MyController">
<form>
<selectngmodel="myForm.car"
ngoptions="obj.idasobj.nameforobjinmyForm.options">
</select>
</form>
<div>
{{myForm.car}}
</div>
</div>
106
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
$scope.myForm={};
$scope.myForm.car="nissan";
$scope.myForm.options=[
{id:"nissan",name:"Nissan"}
,{id:"toyota",name:"Toyota"}
,{id:"fiat",name:"Fiat"}
];
});
</script>
It defines obj as each object in the myForm.options array. Thus, an option element will
be generated from each element in the myForm.options array in the $scope object.
107
This defines the obj.id property of each object as the value of each option element
generated, and the obj.nameproperty as the label. If you want the value and label to
come from the same property, just leave out the asobj.name part (the label part of the
expression).
You can call functions on the $scope object from inside both the optionBinding
expression and dataSourceexpression. Here is an example:
obj.idasgetLabelName(obj)forobjingetOptionArray()
This example will use the value returned from the getLabelName(obj) function call as
label, and will iterate the objects returned by the getOptionArray() function call.
You can create option groups ( <optgroup> HTML elements with option elements
inside) by adding a groupbysection to the optionBinding expression. Here is an
example:
obj.idasobj.namegroupbyobj.type
This example will group the generated option elements into optgroup elements using
the obj.type property to determine which option elements to group together. Objects
with the same value in the obj.type property will be grouped into the
same optgroup element.
You can also iterate the properties of an object instead of iterating an array of objects.
Here is an example:
propNameaspropValuefor(propName,propValue)inobjectWithProperties
This example will bind the property name as the option element value and the property
value as the label, of all properties in the $scope.objectWithProperties object.
108
Empty Options
If the value set for a select box in the $scope object by the controller function does not
match a value attribute of any of the option elements, AngularJS inserts an
empty option element in the select box.
You can set a label for this empty option element by inserting an option element in the
HTML, like this:
<form>
<selectngmodel="myForm.car"
ngoptions="obj.idasobj.nameforobjinmyForm.options">
<optionvalue="">Pleasechooseacar</option>
</select>
</form>
109
<div>
{{myForm.car}}
</div>
</div>
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
$scope.myForm={};
$scope.myForm.car=["nissan"];
$scope.myForm.options=[
{id:"nissan",name:"Nissan"}
,{id:"toyota",name:"Toyota"}
,{id:"fiat",name:"Fiat"}
];
});
</script>
Form Validation
AngularJS has a set of form validation directives you can use. AngularJS validates form
fields before copying their value into the $scope properties to which the form fields are
bound. If a form field is invalid, its value is not copied into the $scope property it is bound
110
to. Instead the corresponding $scope property is cleared. That is done to prevent
the$scope properties from containing invalid values.
Each of the form validating directives are covered in the following sections.
ng-minlength + ng-maxlength
The ngminlength and ngmaxlength form validation directives can be used to validate
the length of data entered in a form field. Here is an example:
<divngcontroller="MyController">
<form>
<inputtype="text"id="name"ngmodel="myForm.name"ngminlength="5"ng
maxlength="12">Name<br/>
</form>
<div>
{{myForm.name}}
</div>
</div>
<script>
angular.module("myapp",[])
.controller("MyController",function($scope){
$scope.myForm={};
$scope.myForm.name="JakobJenkov";
});
</script>
111
This example sets the ngminglength to 5 and ngmaxlength to 12. That means that if
the text in the input field is less than 5 or more than 12 characters long, the value from
the input field will not be copied into the $scope.myForm.name property. You can try this
example yourself and see what happens.
Notice the div element which displays the value of the $scope.myForm.name. This wil
show you what value was copied from the text field into
the $scope.myForm.name property. Notice how it is empty when the text field contains
less than 5 or more than 12 characters.
ng-pattern
The ngpattern directive can be used to validate the value of an input field against a
regular expression. Here is an example:
<inputtype="text"id="name"ngmodel="myForm.name"ngpattern="/^\d+$/">Name
<br/>
The regular expressions must follow the JavaScript regular expression syntax. This
example defines a regular expression that matches strings of digits containing at least 1
digit.
ng-required
The ngrequired directive checks if the value of the form field is empty or not. Actually,
you just use the requiredattribute of HTML5, and AngularJS detects it automatically.
112
</form>
When you call a function on the $scope object (a function added to the $scope object by
your controller function), you can access the ngFormController object via its name, like
this:
$scope.myFormNg
If you add a name attribute to the form fields inside the form,
their ngModelController objects will be accessible as properties on
the ngFormController object. Here is an example:
<formname="myFormNg"ngsubmit="myForm.submitTheForm()">
<inputname="firstName"type="text"ngmodel="myForm.firstName">
</form>
You can now access the ngModelController of the firstName input field like this:
$scope.myFormNg.firstName
Property
Description
$pristine
True if the form has not been changed (no form fields has changed),
false if some fields have been changed.
$dirty
The reverse of $pristine - false if the form has not been changed 113
true if it has.
$valid
True if the form field (or the whole form = all form fields) is valid. False
if not.
$invalid
The reverse of the $valid - false if the field (or all fields in the form) is
valid, true if the field (or a single field in the for) is invalid.
You can use these properties to set a matching CSS class on the input fields. Here is an
example :
<style>
.fieldValid{
border:1pxsolid#00ff00;
}
.fieldInvalid{
border:1pxsolid#ff0000;
}
</style>
<divngcontroller="MyController">
<formname="myFormNg">
<inputtype="text"ngclass="myForm.getFormFieldCssClass(myFormNg.name)"
114
id="name"name="name"ngmodel="myForm.name"ngminlength="2">Name
<br/>
</form>
</div>
<script>
angular.module("myapp",[])
.controller("MyController",function($scope,$http){
$scope.myForm={};
$scope.myForm.getFormFieldCssClass=function(ngModelController){
//console.log("gettingcssclass:"+ngModelController.
$valid);
if(ngModelController.$pristine)return"";
returnngModelController.$valid?"fieldValid":
"fieldInvalid";
}
});
</script>
Notice the ngclass directive on the input field. This directive calls
the myForm.getFormFieldCssClass()function using myFormNg.name as parameter. This is
the ngModelController for the name input element.
ThemyForm.getFormFieldCssClass() just returns the matching CSS class as a string.
You can also use the validation properties to show or hide div elements with validation
messages. Here is the form from before, with a div added:
115
<divngcontroller="MyController">
<formname="myFormNg"ngsubmit="myForm.submitTheForm()"novalidate>
<inputtype="text"ngclass="myForm.getFormFieldCssClass(myFormNg.name)"
id="name"name="name"ngmodel="myForm.name"ngminlength="2">Name
<br/>
<divngshow="myFormNg.name.$invalid">
Youmustenteravalidname.
</div>
</form>
</div>
Notice the ngshow directive on the div element. This directive uses the value of
the myFormNg.name.$invalidvalidation property to determine if the div should be shown
or not.
Remember that you can access the $pristine, $dirty, $valid and $invalid properties
of thengFormController too. These properties contain the validation state of the whole
form. This button code example disables the submit button if the form is invalid:
<buttonngdisabled="myFormNg.$invalid">SubmitForm</button>
Submitting Forms
You can submit a form in two ways:
In both cases a JavaScript function is called on the $scope object. You attach this
JavaScript function to the $scopeobject in your controller function. The JavaScript
function should send the data from the form to your server via AJAX.
Here is a form that uses the ngclick attribute on a button element:
<divngcontroller="MyController">
<form>
<inputtype="text"id="name"ngmodel="myForm.name"ngminlength="5"ng
maxlength="12">Name<br/>
<selectngmodel="myForm.car">
<optionvalue="nissan">Nissan</option>
<optionvalue="toyota">Toyota</option>
<optionvalue="fiat">Fiat</option>
</select>
<buttonngclick="myForm.submitTheForm()">SubmitForm</button>
</form>
<div>
{{myForm.name}}
</div>
<div>
{{myForm.car}}
</div>
</div>
117
<script>
angular.module("myapp",[])
.controller("MyController",function($scope,$http){
$scope.myForm={};
$scope.myForm.name="JakobJenkov";
$scope.myForm.car="nissan";
$scope.myForm.submitTheForm=function(item,event){
console.log(">Submittingform");
vardataObject={
name:$scope.myForm.name
,car:$scope.myForm.car
};
varresponsePromise=$http.post("/angularjsexamples/jsontestdata.jsp",
dataObject,{});
responsePromise.success(function(dataFromServer,status,headers,config){
console.log(dataFromServer.title);
});
responsePromise.error(function(data,status,headers,config){
alert("Submittingformfailed!");
});
}
118
});
</script>
Notice how the ngclick attribute points to the myForm.submitTheForm() function, and
notice how thesubmitTheForm() function is attached to the $scope object inside the
controller function.
Here is the same form using an ngsubmit attribute to submit the form:
<formngsubmit="myForm.submitTheForm()">
<inputtype="text"id="name"ngmodel="myForm.name"ngminlength="5"ng
maxlength="12">Name<br/>
<selectngmodel="myForm.car">
<optionvalue="nissan">Nissan</option>
<optionvalue="toyota">Toyota</option>
<optionvalue="fiat">Fiat</option>
</select>
<inputtype="submit"value="SubmitForm">
</form>
As you can see, the two mechanisms are very similar. They both call a submit function
on the $scope object.
119
$timeout
o Injecting $timeout
o
$interval
o
Injecting $interval
AngularJS has two timer services, $timeout and $interval, which you can use to call
functions in your application. The $timeout and $interval services are similar in their
functionality to JavaScript's setTimeout() andsetInterval() functions (actually
belonging to the window object). The functionality of these services is also similar, so I
will cover both in this text.
$timeout
The $timeout service can be used to call another JavaScript function after a given time
delay. The $timeout service only schedules a single call to the function. For repeated
calling of a function, see $interval later in this text.
Injecting $timeout
To use the $timeout service you must first get it injected into a controller function. Here
is an example that injects the$timeout service into a controller function:
varmyapp=angular.module("myapp",[]);
myapp.controller("MyController",function($scope,$timeout){
120
});
Notice the $timeout parameter of the controller function. Into this parameter
the $timeout service will be injected by AngularJS, just like any other AngularJS service
you would want to use in your controller function.
myapp.controller("MyController",function($scope,$timeout){
$timeout(callAtTimeout,3000);
});
functioncallAtTimeout(){
console.log("Timeoutoccurred");
}
121
myapp.controller("DIController",function($scope,$timeout){
$scope.callAtTimeout=function(){
console.log("$scope.callAtTimeoutTimeoutoccurred");
}
$timeout(function(){$scope.callAtTimeout();},3000);
});
Notice the function passed to the $timeout service. This function calls
the callAtTimeout() function on the$scope object.
$interval
The $interval service is similar in function to the $timeout service, except it schedules
a function for repeated execution with a time interval in between.
Injecting $interval
To use the $interval service you must have it injected into a controller function. Here is
an example that injects the$interval service into a controller function:
varmyapp=angular.module("myapp",[]);
myapp.controller("MyController",function($scope,$interval){
});
122
As you can see, it is very similar to how you inject any other service in AngularJS.
myapp.controller("MyController",function($scope,$interval){
$interval(callAtInterval,5000);
});
functioncallAtInterval(){
console.log("Intervaloccurred");
}
myapp.controller("DIController",function($scope,$interval){
123
$scope.callAtInterval=function(){
console.log("$scope.callAtIntervalIntervaloccurred");
}
$interval(function(){$scope.callAtInterval();},3000);
});
The function passed to the $interval service calls the callAtInterval() function on
the $scope object.
124
$interval(function(){$scope.callAtInterval();},3000,false);
A Basic Directive
Directive Types
You can implement the following types of directives:
125
Element directives
Attribute directives
Comment directives
Of these, AngularJS recommends that you try to use element and attribute directives,
and leave the CSS class and comment directives (unless absolutely necessary).
The type of a directive determines how the directive is activated. An element directive is
activated when AngularJS finds a matching HTML element in the HTML template. An
attribute directive is activated when AngularJS finds a matching HTML element attribute.
A CSS class directive is activated when AngularJS finds a matching CSS Class. And, a
comment directive is activated when AngularJS finds a matching HTML comment.
A Basic Directive
You register a directive with a module. Here is an example of how that looks:
myapp=angular.module("myapp",[]);
myapp.directive('div',function(){
vardirective={};
directive.restrict='E';/*restrictthisdirectivetoelements*/
directive.template="Myfirstdirective:{{textToInsert}}";
returndirective;
});
126
Notice the call to the directive() function on the module. When you call this function
you can register a new directive. The first parameter to the directive() function call is
the name of the directive to register. This name is what you use in your HTML templates
when you want to activate the directive. In this example I have used the name ' div'
which means that the directive is activated every time an HTML element named div is
found in the HTML template.
The second parameter passed to the directive function is a factory function. This
function should return a directive definition when invoked. AngularJS will invoke this
function to obtain a JavaScript object which contains the definition of the directive. If you
look inside the function in the above example you will see that it does indeed return a
JavaScript object.
The JavaScript object returned from the factory function has two properties:
A restrict field and a template field.
The restrict field is used to set if the directive should be activated by a matching
HTML element, or an element attribute. By setting restrict to E you specify that only
HTML elements named div should activate the directive. By setting restrict to A you
specify that only HTML attributes named div should activate the directive. You can also
use a value of AE which will match both HTML element names and attribute names.
The template field is an HTML template that will replace the content of the
matched div element. It will work as if the content of the matched div element had not
been there, and instead this HTML template had been located in the same place.
Imagine that your HTML page has this HTML:
<divngcontroller="MyController">
<div>Thisdivwillbereplaced</div>
</div>
Then the added directive would be activated when AngularJS finds the
inner div element. Instead of this div element, this HTML will be inserted:
Myfirstdirective:{{textToInsert}}
127
myapp.directive('div',function(){
vardirective={};
directive.restrict='E';/*restrictthisdirectivetoelements*/
directive.template="Myfirstdirective:{{textToInsert}}";
returndirective;
});
In case that HTML template grows big, it is gets hard to write and maintain the HTML
inside a JavaScript string. You can then put the HTML into its own file and have
AngularJS load it from that file. You do so by putting the URL of the HTML template file
into the templateUrl property of the directive definition object. Here is an example:
myapp=angular.module("myapp",[]);
128
myapp.directive('div',function(){
vardirective={};
directive.restrict='E';/*restrictthisdirectivetoelements*/
directive.templateUrl="/myapp/htmltemplates/divtemplate.html";
returndirective;
});
AngularJS will now load the HTML template from the URL set in
the templateUrl property.
Using the separate HTML template file and the templateUrl property is especially useful
when you create more specialized directives, like a directives showing user info. Here is
an example:
myapp=angular.module("myapp",[]);
myapp.directive('userinfo',function(){
vardirective={};
directive.restrict='E';/*restrictthisdirectivetoelements*/
directive.templateUrl="/myapp/htmltemplates/userinfotemplate.html";
returndirective;
});
129
Then the two <userinfo> elements would be replaced by the same HTML template,
which is bound to the same$scope variable. The result would be that the
two <userinfo> elements would be replaced by the exact same HTML code.
To be able to bind the two <userinfo> elements to different values in the $scope object,
you need to bind the HTML template to an isolate scope.
An isolate scope is a separate scope object tied to the directive. Here is how you define
it in the directive definition object:
myapp.directive('userinfo',function(){
vardirective={};
directive.restrict='E';
directive.template="User:{{user.firstName}}{{user.lastName}}";
130
directive.scope={
user:"=user"
}
returndirective;
})
Notice how the HTML template has two interpolation directives bound
to {{user.firstName}} and{{user.lastName}}. Notice the user. part. And notice
the directive.scope property. The directive.scopeproperty is a JavaScript object
which contains a property named user. The directive.scope property is the isolate
scope object, and the HTML template is now bound to the directive.scope.user object
(via the{{user.firstName}} and {{user.lastName}} interpolation directives).
The directive.scope.user property is set to "=user". That means, that
the directive.scope.user property is bound to the property in the scope property (not
in the isolate scope) with the name passed to the user attribute of
the <userinfo> element. It sounds confusing, so look at this HTML example:
<userinfouser="jakob"></userinfo>
<userinfouser="john"></userinfo>
These two <userinfo> element contain a user attribute. The value of these attributes
contain the names of properties in the $scope object which are to be referenced by the
isolate scope object's userinfo property.
Here is a full example:
<userinfouser="jakob"></userinfo>
<userinfouser="john"></userinfo>
131
<script>
myapp.directive('userinfo',function(){
vardirective={};
directive.restrict='E';
directive.template="User:<b>{{user.firstName}}</b>
<b>{{user.lastName}}</b>";
directive.scope={
user:"=user"
}
returndirective;
});
myapp.controller("MyController",function($scope,$http){
$scope.jakob={};
$scope.jakob.firstName="Jakob";
$scope.jakob.lastName="Jenkov";
$scope.john={};
$scope.john.firstName="John";
132
$scope.john.lastName="Doe";
});
</script>
directive.restrict='E';/*restrictthisdirectivetoelements*/
133
directive.compile=function(element,attributes){
//doonetimeconfigurationofelement.
varlinkFunction=function($scope,element,atttributes){
//bindelementtodatain$scope
}
returnlinkFunction;
}
returndirective;
});
</script>
The compile() function takes two parameters: The element and attributes parameters.
The element parameter is a jqLite wrapped DOM element. AngularJS contains a lite
version of jQuery to help you do DOM manipulation, so the element's DOM manipulation
methods are the same as you know from jQuery.
The attributes parameter is a JavaScript object containing properties for all the
attributes of the DOM element. Thus, to access an attribute named type you would
write attributes.type.
The link() function takes three parameters: The $scope parameter,
the element parameter and the attributesparameter.
The element and attributes parameter is the same as passed to
134
the compile() function. The$scope parameter is the normal scope object, or an isolate
scope in case you have specified one in the directive definition object.
The compile() and link() function names are actually confusing. They are inspired by
compiler terms. I can see the resemblance, but a compiler parses an input once, and
creates an output. A directive configures an HTML element and then updates that HTML
subsequently whenever the $scope object changes.
A better name for the compile() function would have been something
like create(), init() or configure(). Something that signals that this function is only
called once.
A better name for the link() function would have been something
like bind() or render(), which signals that this function is called whenever the directive
needs to bind data to it, or to re-render it.
Here is a full example that shows a directive that uses both
a compile() and link() function:
<divngcontroller="MyController">
<userinfo>Thiswillbereplaced</userinfo>
</div>
<script>
myapp=angular.module("myapp",[]);
myapp.directive('userinfo',function(){
vardirective={};
directive.restrict='E';/*restrictthisdirectivetoelements*/
directive.compile=function(element,attributes){
135
element.css("border","1pxsolid#cccccc");
varlinkFunction=function($scope,element,attributes){
element.html("Thisisthenewcontent:"+$scope.firstName);
element.css("backgroundcolor","#ffff00");
}
returnlinkFunction;
}
returndirective;
})
myapp.controller("MyController",function($scope,$http){
$scope.cssClass="notificationDiv";
$scope.firstName="Jakob";
$scope.doClick=function(){
console.log("doClick()called");
}
});
</script>
The compile() function sets a border on the HTML element. This is only executed once
because the compile()function is only executed once.
136
The link() function replaces the content of the HTML element, and sets the
background color to yellow.
There is no particular reason why the border was set in the compile() function, and the
background color in thelink() function. Both could have been set in
the compile() function, or both in the link() function. If set in thecompile() function
they would only have been set once (which is often what you want). If set in
the link() function they would be set every time the HTML element is bound to data in
the $scope object. This might be useful if you needed to set the border and background
color differently depending on data in the $scope object.
<script>
myapp=angular.module("myapp",[]);
myapp.directive('userinfo',function(){
vardirective={};
directive.restrict='E';/*restrictthisdirectivetoelements*/
directive.link=function($scope,element,attributes){
element.html("Thisisthenewcontent:"+$scope.firstName);
137
element.css("backgroundcolor","#ffff00");
}
returndirective;
})
myapp.controller("MyController",function($scope,$http){
$scope.cssClass="notificationDiv";
$scope.firstName="Jakob";
$scope.doClick=function(){
console.log("doClick()called");
}
});
</script>
Notice how the link() function does the same as the link() function returned in the
previous example.
138
The directive is marked by the <mytransclude> element. But the content inside it is set
by the developer. Thus, this part of the HTML should not be replaced by the directive's
HTML template. We actually want that part of the HTML to be processed by AngularJS.
This processing is called "transclusion".
In order to make AngularJS process the HTML inside a directive, you have to set
the transclude property of the directive definition object to true. You will also have to
tell AngularJS what part of the directive's HTML template that is to contain the
transcluded HTML. You do so by inserting the ngtransclude attribute (a directive,
really) into the HTML element in the HTML template where you want the transcluded
HTML inserted.
Here is an AngularJS directive that shows how to use transclusion:
<mytransclude>Thisisatranscludeddirective{{firstName}}</mytransclude>
<script>
myapp=angular.module("myapp",[]);
myapp.directive('mytransclude',function(){
vardirective={};
directive.restrict='E';/*restrictthisdirectivetoelements*/
directive.transclude=true;
directive.template="<divclass='myTransclude'ngtransclude></div>";
returndirective;
});
myapp.controller("MyController",function($scope,$http){
$scope.firstName="Jakob";
139
});
</script>
Notice the HTML inside the <mytransclude> element. This HTML code contains the
interpolation directive{{firstName}}. We want AngularJS to process this HTML for us
so that interpolation directive is executed. To achieve that I have set
the transclude property to true on the directive definition object. I have also inserted
an ngtransclude attribute into the HTML template. This attribute tells AngularJS what
element to insert the transcluded HTML into.
Value
o
Factory
o
Service
o
Injecting a Value
Providers
o
Configuring a Provider
Constants
AngularJS comes with a built-in dependency injection mechanism. You can divide your
application into multiple different types of components which AngularJS can inject into
each other. Modularizing your application makes it easier to reuse, configure and test
the components in your application.
140
Value
Factory
Service
Provider
Constant
These core types can be injected into each other using AngularJS dependency injection
mechanism. Throughout the rest of this text I will explain how to define and inject these
components into each other.
Value
A value in AngularJS is a simple object. It can be a number, string or JavaScript object.
Values are typically used as configuration which is injected into factories, services or
controllers.
A value has to belong to an AngularJS module. Here are three examples that add
values to an AngularJS module:
varmyModule=angular.module("myModule",[]);
myModule.value("numberValue",999);
myModule.value("stringValue","abc");
myModule.value("objectValue",{val1:123,val2:"abc"});
The values are defined using the value() function on the module. The first parameter is
the name of the value, and the second parameter is the value itself. Factories, services
and controllers can now reference these values by their name.
141
Injecting a Value
Injecting a value into an AngularJS controller function is done simply by adding a
parameter with the same name as the value (the first parameter passed to
the value() function when the value is defined). Here is an example:
varmyModule=angular.module("myModule",[]);
myModule.value("numberValue",999);
myModule.controller("MyController",function($scope,numberValue){
console.log(numberValue);
});
Notice how the second parameter of the controller function has the same name as the
value.
Factory
Factory is a function that creates values. When a service, controller etc. needs a value
injected from a factory, the factory creates the value on demand. Once created, the
value is reused for all services, controllers etc. which need it injected. Thus, a factory
differs from a value in that it can use a factory function to create the object it returns.
You can also inject values into a factory for use when creating the object. You cannot do
that with a value.
Here is an example that defines a factory on a module, and a controller which gets the
factory created value injected:
142
varmyModule=angular.module("myModule",[]);
myModule.factory("myFactory",function(){
return"avalue";
});
myModule.controller("MyController",function($scope,myFactory){
console.log(myFactory);
});
As you can see, it is very similar to defining and injecting a value object. Keep in mind
that it is not the factory function that is injected, but the value produced by the factory
function.
myModule.value("numberValue",999);
myModule.factory("myFactory",function(numberValue){
143
return"avalue:"+numberValue;
});
In this example the injected value is used to create the object created by the factory
function.
Service
A service in AngularJS is a singleton JavaScript object which contains a set of functions.
The functions contain whatever logic is necessary for the service to carry out its work.
AngularJS services are created using the service() function on a module. Here is an
example:
functionMyService(){
this.doIt=function(){
console.log("done");
}
}
varmyModule=angular.module("myModule",[]);
myModule.service("myService",MyService);
As you can see, services are defined somewhat differently than factories and values.
First of all, the service is defined as a separate, named function. That is because
services in AngularJS are created using the new keyword. Thus, AngularJS will do this
internally:
144
vartheService=newMyService();
Apart from defining services as functions with functions inside, you add them to and use
them with AngularJS just like you would with a value or function. You inject a service
into a controller like this:
functionMyService(){
this.doIt=function(){
console.log("done");
}
}
varmyModule=angular.module("myModule",[]);
myModule.service("myService",MyService);
myModule.controller("MyController",function($scope,myService){
myService.doIt();
});
145
myModule.value("myValue","12345");
functionMyService(myValue){
this.doIt=function(){
console.log("done:"+myValue;
}
}
myModule.service("myService",MyService);
Notice how the parameter to the MyService function is named the same as the value
registered on the module. Thus, the value will be injected into the service when it is
created.
Providers
Providers in AngularJS is the most flexible form of factory you can create. You register a
provider with a module just like you do with a service or factory, except you use
the provider() function instead. Here is an AngularJS provider example:
varmyModule=angular.module("myModule",[]);
146
myModule.provider("mySecondService",function(){
varprovider={};
provider.$get=function(){
varservice={};
service.doService=function(){
console.log("mySecondService:ServiceDone!");
}
returnservice;
}
returnprovider;
});
As you can see, the provider() function takes 2 parameters. The first parameter is the
name of the service / object which the provider creates. In this case the name
is mySecondService. The second parameter is the function which creates the provider.
Note: The provider is itself a factory, so at this time no actual service or object is created
from the provider. Only the function creating the provider is defined.
When you look at the function creating the provider you can see that the provider is a
JavaScript object.
The JavaScript provider object contains a single $get() function. This is the factory
function of the provider. In other words, the $get() function creates whatever the
provider creates (service, value etc.). In the example above, the provider creates a
147
service object which contains a single service function (standard JavaScript function)
calleddoService().
In order to get the product of a provider injected into a controller, do specify a
dependency on the provider, just like you would with a service. What is injected into the
controller is the product created by the provider, not the provider itself. Here is an
AngularJS provider injection example:
myModule.controller("MyController",function($scope,mySecondService){
$scope.whenButtonClicked=function(){
mySecondService.doIt();
}
});
As you can see, the name of the provider is used as a parameter in the controller
function. The object created by the provider's $get() function will be injected into this
parameter.
Configuring a Provider
It is possible to configure a provider further via calls to its function during the
configuration phase of a module. Here is an example:
varmyModule=angular.module("myModule",[]);
myModule.provider("mySecondService",function(){
varprovider={};
varconfig={configParam:"default"};
148
provider.doConfig=function(configParam){
config.configParam=configParam;
}
provider.$get=function(){
varservice={};
service.doService=function(){
console.log("mySecondService:"+config.configParam);
}
returnservice;
}
returnprovider;
});
myModule.config(function(mySecondServiceProvider){
mySecondServiceProvider.doConfig("newconfigparam");
});
myModule.controller("MyController",function($scope,mySecondService){
149
$scope.whenButtonClicked=function(){
mySecondService.doIt();
}
});
Notice how the provider object now has an extra function called doConfig(). This
function can be used to set a configuration parameter on the provider.
Notice also the call to the myModule.config() function. The config function takes a
function as parameter. This function can configure the module. The function passed
to config() takes a single parameter namedmySecondServiceProvider. That is the
same name the provider is registered with plus Provider as suffix. The suffix tells
AngularJS to inject the provider itself, and not the object created by the provider. Inside
the function passed to the config() function
the mySecondServiceProvider.doConfig() function is called, which sets the config
parameter on the provider.
The controller defined later in the example just depends on the object created by the
provider (not the provider itself). It does so by taking a parameter
named mySecondService which is the name the provider of the service is registered with.
As you can see, the service used from inside the $scope.whenButtonClicked() function.
Constants
In the previous section on providers you saw how to configure a provider via
the module.config() function. Unfortunately you cannot inject values into
the module.config() function. Instead you can inject constants.
Constants in AngularJS are defined using the module.constants() function. Here is an
AngularJS constant example:
150
myModule.constant("configValue","constantconfigvalue");
This constant can now be injected into the module.config() function like this:
myservices.config(function(mySecondServiceProvider,configValue){
mySecondServiceProvider.doConfig(configValue);
});
As you can see, the parameter configValue matches the name of the constant which is
also configValue. Thus the value of the constant will be injected into this parameter.
The constant value is then passed as parameter to the doConfig() function on
the mySecondServiceProvider provider.
myUtilModule.value("myValue","12345");
varmyOtherModule=angular.module("myOtherModule",['myUtilModule']);
myOtherModule.controller("MyController",function($scope,myValue){
151
Notice how the second module (myOtherModule) lists the name of the first module
(myUtilModule) in the second parameter (inside the array) passed to
the angular.module() function. This tells AngularJS that all values, factories and
services defined inside the myUtilModule should be available inside
the myOtherModule module too. In other words, myOtherModule depends
on myUtilModule.
Second, notice how the MyController controller function now declares a parameter
named myValue. This value will be provided from the value registered on
the myUtilModule module.
myapp.controller("AController",['$scope',function(p1){
p1.myvar="thevalue";
}]);
This example injects the $scope object into the p1 parameter of the controller function.
152
Notice how the controller function is registered. Instead of passing the controller function
to theangular.controller function directly, an array is passed instead. This array
contains the name of the value to inject into the controller function, as well as the
controller function itself. The controller function is always the last value in this array. If
you need to inject more than one value, the value names are listed in the beginning of
the array and in the sequence they are to be injected into the function. Here is a
minification safe multi value example:
varmyapp=angular.module("myapp",['myservices']);
myapp.controller("AController",['$scope','$http',function(p1,p2){
p1.myvar="thevalue";
p2.get("/myservice.json");
}]);
This example injects the $scope object into the p1 parameter, and the $http service into
the p2 parameter of the controller function.
Now it no longer matters what the parameter names of the controller function are.
AngularJS will use the strings in the beginning of the array to determine what to inject
into the controller function.
The same mechanism can be used for factories, services and providers to provide
minification safe dependency injection. Here is a minification safe factory, service and
provider example:
varmyutil=angular.module("myutil",[]);
myutil.value("safeValue","asafevalue");
153
myutil.factory("safeFactory",['safeValue',function(p1){
return{value:p1};
}]);
functionMySafeService(p1){
this.doIt=function(){
return"MySafeService.doIt()called:"+p1.value;
}
}
myutil.service("safeService",['safeFactory',MySafeService]);
myutil.provider("safeService2",function(){
varprovider={};
provider.$get=['safeService',function(p1){
varservice={};
service.doService=function(){
console.log("safeServicefromprovider:"+p1.doIt());
}
154
returnservice;
}];
returnprovider;
});
myapp.controller("AController",['$scope','safeService2',function(p1,p2){
p1.myvar="thevalue";
p2.doService();
}]);
Notice especially the declaration of the provider. Notice how the dependencies are not
specified on the provider factory function, but on the $get() function of the provider
returned from inside the provider factory function. Actually, an array with names of
dependencies and the function implementation is used instead of just a $get() function.
Other than that, specifying dependencies works the same for providers as for factories,
services and controller functions.
Links to Routes
Route Parameters
155
AngularJS routes enable you to create different URLs for different content in your
application. Having different URLs for different content enables the user to bookmark
URLs to specific content, and send those URLs to friends etc. In AngularJS each such
bookmarkable URL is called a route.
AngularJS routes enables you to show different content depending on what route is
chosen. A route is specified in the URL after the # sign. Thus, the following URL's all
point to the same AngularJS application, but each point to different routes:
http://myangularjsapp.com/index.html#books
http://myangularjsapp.com/index.html#albums
http://myangularjsapp.com/index.html#games
http://myangularjsapp.com/index.html#apps
When the browser loads these links, the same AngularJS application will be loaded
(located athttp://myangularjsapp.com/index.html), but AngularJS will look at the route
(the part of the URL after the #) and decide what HTML template to show.
At this point it may sound a little abstract, so let us look at a fully working AngularJS
route example:
<!DOCTYPEhtml>
<htmllang="en">
<head>
<title>AngularJSRoutesexample</title>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js"></scrip
t>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular
route.min.js"></script>
</head>
156
<bodyngapp="sampleApp">
<ahref="#/route1">Route1</a><br/>
<ahref="#/route2">Route2</a><br/>
<divngview></div>
<script>
varmodule=angular.module("sampleApp",['ngRoute']);
module.config(['$routeProvider',
function($routeProvider){
$routeProvider.
when('/route1',{
templateUrl:'angularroutetemplate1.jsp',
controller:'RouteController'
}).
when('/route2',{
templateUrl:'angularroutetemplate2.jsp',
controller:'RouteController'
}).
otherwise({
redirectTo:'/'
157
});
}]);
module.controller("RouteController",function($scope){
})
</script>
Each part of this sample application will be explained in the following sections.
The AngularJS Route module is contained in its own JavaScript file. To use it we must
include in our AngularJS application.
Inside the div with the ngView directive (can also be written ngview) the HTML template
specific to the given route will be displayed.
159
otherwise({
redirectTo:'/'
});
}]);
</script>
Links to Routes
The final thing to notice in this example is the two links in the HTML page:
<ahref="#/route1">Route1</a><br/>
<ahref="#/route2">Route2</a><br/>
160
Notice how the part of the URLs after the # matches the routes configured on
the $routeProvider.
When one of these links is clicked, the URL in the browser window changes, and
the div with the ngView directive will show the HTML template matching the route path.
Route Parameters
You can embed parameters into the route path. Here is an AngularJS route path
parameter example:
#/books/12345
This is a URL with a route path in. In fact it pretty much consists of just the route path.
The parameter part is the 12345which is the specific id of the book the URL points to.
AngularJS can extract values from the route path if we define parameters in the route
paths when we configure the$routeProvider. Here is the example $routeProvider from
earlier, but with parameters inserted into the route paths:
<script>
module.config(['$routeProvider',
function($routeProvider){
$routeProvider.
when('/route1/:param',{
templateUrl:'angularroutetemplate1.jsp',
controller:'RouteController'
}).
when('/route2/:param',{
templateUrl:'angularroutetemplate2.jsp',
controller:'RouteController'
161
}).
otherwise({
redirectTo:'/'
});
}]);
</script>
Both of the URLs in the when() calls now define a parameter. It is the part starting from
the colon (:param)
AngularJS will now extract from the URL (route path) whatever comes after
the #/route1/ part. Thus, from this URL:
#/route1/12345
Notice how the controller function takes the $routeParams service as parameter, and
then copies the parameter named param into the $scope.param property. Now your
AngularJS views can get access to it, or you can use it in AJAX calls etc.
Here is a full AngularJS route parameter example:
<!DOCTYPEhtml>
162
<htmllang="en">
<head>
<title>AngularJSRoutesexample</title>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js"></scrip
t>
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular
route.min.js"></script>
</head>
<bodyngapp="sampleApp">
<ahref="#/route1/abcd">Route1+param</a><br/>
<ahref="#/route2/1234">Route2+param</a><br/>
<divngview></div>
<script>
varmodule=angular.module("sampleApp",['ngRoute']);
module.config(['$routeProvider',
function($routeProvider){
$routeProvider.
when('/route1/:param',{
templateUrl:'angularroutetemplate1.jsp',
163
controller:'RouteController'
}).
when('/route2/:param',{
templateUrl:'angularroutetemplate2.jsp',
controller:'RouteController'
}).
otherwise({
redirectTo:'/'
});
}]);
module.controller("RouteController",function($scope,$routeParams){
$scope.param=$routeParams.param;
})
</script>
</body>
</html>
Table of Contents
Internationalization in Filters
o The Date Filter
o
AngularJS has built-in support for internationalization of numbers and dates. In this text I
will take a look at how they work.
Internationalization in Filters
Some of the built-in AngularJS filters supports internationalization. For instance,
the date and currency filters have built-in support for internationalization. Here is how
you would normally use these filters:
{{theDate|date:'fullDate'}}
{{theValue|currency}}
{{theValue|number}}
The date filter will format the variable theDate as a date according to the locale chosen
in the web app. The same is true for the currency and number filter.
Filters are covered in more detail in the section about filtering in my views and directives
tutorial: AngularJS Filtering.
short
medium
fullDate
165
shortDate
mediumDate
longDate
shortTime
mediumTime
Notice how the last two examples use the HTML entities for pounds and euros.
166
Include this after including the AngularJS main JavaScript file, and it should work out of
the box. There nothing more that needs to be done than including this file.
To find the correct locale for your country, look at code.angularjs.org, click on the
version of AngularJS you are using, and then click on the i18n directory. In that directory
you see a list of all the available locales. Find the one that matches the country or
contries you want to support, and then download them and include them in your HTML
page (or reference them directly at code.angularjs.org).
167
<bodyngapp="myapp">
AngularJSI18n
<divngcontroller="mycontroller">
{{theDate|date:"fullDate"}}<br/>
{{theValue|currency}}
</div>
<script>
varmodule=angular.module("myapp",[]);
module.controller("mycontroller",function($scope){
$scope.theDate=newDate();
$scope.theValue=123.45;
});
</script>
</body>
168
</html>
Critique Summary
An Alternative
Over the years I have looked at Java Swing, SWT, Flex, GWT, jQuery, SmartClient, JSP,
Wicket, Struts, Spring MVC and now AngularJS, in my long search for the "perfect" GUI
toolkit for web apps. I hoped AngularJS would be the end of my search, but right from
the beginning something in AngularJS smelled wrong to me. It took me some time
before I was able to put my finger on just what it is I don't like.
Don't get me wrong. You can make web apps just fine with AngularJS. There are just
some aspects I don't like - choices I don't agree with. These are:
1.
2.
While this style works just fine for the simple cases, it works less well for more advanced
cases.
In web apps the code has to insert data into the HTML page somehow. You can
generate the whole HTML page using an imperative language, like Java (on the server)
or JavaScript (in the browser). Or, you can try to "teach HTML new tricks" so HTML
becomes able to do some of the things you can do in an imperative language. Or, you
find a middle ground in which the more static parts of the HTML page is defined using
HTML syntax, and the more advanced parts are created using a imperative language.
HTML is a declarative language, so another way to put it is, that either you mix a bit of
the imperative into the declarative, or a bit of the declarative into the imperative. I will get
back to this again later.
In my opinion generating HTML on the client cases works better than generating HTML
on the server in most cases. It results in cleaner HTML generation code, and cleaner
backend code too. This means, that the imperative language should run in the browser.
In AngularJS that language is JavaScript and that is just fine.
170
Using declarative HTML templates with a bit of data binding injected is not a new idea. It
has been tried before in JSP (JavaServer Pages). Here is an example:
<span><%=myObject.getMyProperty()%></span>
This data binding injection style is incredibly simple for the simple cases, and thus
immediately appeals to most developers. However, as soon as your web app gets more
advanced you realize that you need some kind of imperative control logic to direct the
HTML generation. Two imperative constructs you will soon need is looping and
branching (if-statements). Here is how looping looks in JSP in the first versions:
<%
for(inti=0;i<list.size();i++){
MyObjectmyObject=(MyObject)list.get(i);
%>
<span><%=myObject.getMyProperty()%></span>
<%}%>
Notice how the <% %> markers switches between Java and HTML. While this allowed
imperative control logic to be mixed with the declarative HTML, developers soon
realized that mixing Java directly into the HTML resulted in really messy code when the
HTML pages got more advanced. The syntaxes of Java and HTML just didn't
complement each other very well.
To solve that problem the JSP standard added a concept called "Tag Libraries". Tag
libraries used an XML syntax to allow imperative control to be mixed with the HTML.
Early frameworks like Struts utilized this to create tag libraries for data binding, iteration,
branches etc. Here is an early iteration example:
171
<logic:iterateid="theBean"name="beans">
<span><bean:writename="theBean"property="theProperty"/></span>
</logic:iterate>
This way the syntax of the imperative control logic complements HTML better. However,
you run into other problems instead. First, it is not so easy to express advanced
constructs like recursive iteration of tree or graph data structures. Second, your HTML
template gets messy when you insert too much imperative logic into it. Third, the XML
syntax does not support imperative control structures so well. Expressing them in XML
is more clumsy and verbose than using a syntax specifically designed for that purpose.
It did not end so well for JSP. The Java community learned that there were better
approaches. First were called "Model 2" (early Struts and Spring MVC), and later
"Component Oriented Frameworks" (Wicket and JSF - JavaServer Faces). The last time
I checked I could not even find JSP in Oracle' official Java Enterprise Edition tutorial.
It is easy to ascribe the demise of JSP to the fact that it was generating HTML on the
server. However, that is only part of the problems with JSP.
When you try to use a declarative language for imperative purposes, or an imperative
language to for declarative purposes, you end up with what I call "The declarative /
imperative paradigm mismatch". This mismatch often results in clumsy or ugly code.
It is easy to think that the declarative / imperative mismatch can be avoided with a
programming language that is designed to handle both paradigms. But the problem
goes deeper than the language syntax.
It is not the syntax of a programming language that decides if the language is
declarative or imperative. It is the semantic meaning of what the language describes
that decides between declarative and imperative. You could invent a more Java-like
syntax to describe HTML elements. Or a more HTML-like syntax to describe Java
instructions. But such syntaxes would not change the fact that one language describes
state (HTML documents) and the other language describe commands (operations).
Thus, a Java-like syntax for describing HTML documents would still be declarative, and
a more HTML-like syntax for describing Java-operations would still be imperative.
Think about it for just a moment. JavaScript actually already has a declarative
mechanism built in for defining JavaScript objects. It's JSON - JavaScript Object
Notation. You could actually mimic HTML using JSON. Here is an example:
{
html:{
head:{title:"JSONmimickingHTML"},
body:{
text:"HelloWorld"
}
}
}
Of course this doesn't mimic HTML attributes or even the combination of text and HTML
elmeents possible in the body of an HTML element. But I am sure the JSON syntax
could have been extended to cover that.
173
Imagine you had to generate a JSON object using JavaScript - by mixing JavaScript
instructions in-between the JSON notation. Regardless of the JavaScript-like syntax, it
would be a hard problem to solve elegantly.
Now we are down to what the declarative / imperative paradigm mismatch is really
about. It's just hard to design one language that can both describe state and support
dynamic generation of state intertwined, regardless of the syntax. JSP is a good
example of that. The above JavaScript / JSON example is a good example of that.
XSL is another fine example of the the declarative / imperative mismatch. XSL
attempted to be a declarative language for describing XML document transformations.
Again, it worked beautifully for simple transformations, but once you got to more
advanced cases, XSL broke own. You had to resort to an imperative language for those
parts.
AngularJS is using a similar approach with its directives. AngularJS directives are either
HTML elements, attributes, CSS classes or HTML comments which trigger imperative
actions in their respective JavaScript implementations. AngularJS directives are similar
to JSP tag libraries and are thus prone to the same problems. I find it puzzling that
some developers (in the Java community at least) wrinkle their noses at JSP but praise
AngularJS. Despite the amount of rational thinking our jobs require, the IT industry is far
from always behaving rationally - but that is another topic for another article.
174
that you have to resort to workarounds to get that sort of stuff to work. HTML just wasn't
designed for imperative operations.
This diagram illustrates how a modern HTML5 / CSS3 / JavaScript GUI may contain
both simple and complex components, some of which are best defined using HTML,
and some of which are best rendered via JavaScript.
Finding the right balance between how much is done declaratively and how much is
done imperatively is not easy. Somehow we have to bridge or combine these two
paradigms. But where on the axis is the optimal balance?
As mentioned earlier I have worked with several different GUI toolkits. In my opinion
what has worked best is when you use a declarative approach for the overall
175
composition of the GUI, and use an imperative approach when rendering the more
complex GUI components. Look at this HTML template:
<html>
<head>
</head>
<body>
<div>
<div>MyApp</div>
<div><tableid="theTable"jqctype="jqcTable"></table></div>
</div>
</body>
</html>
It contains a label (MyApp) which is easily declared and positioned using HTML. And it
contains a data table which is a more complex GUI component, so only its location in
the GUI is declared in the HTML. How it is rendered, bound to data and how it behaves
when the user clicks rows etc. is implemented in JavaScript.
This model contains a clear separation between the declarative and imperative
paradigm. At least in the HTML template part. The implementations of the various
components like the data table will be more muddy. But at least that is shielded from the
developer making the HTML template. You are not tempted to specify part of it using
HTML, and part of it using HTML-like directives. And you do not have to specify the
whole GUI composition imperatively either.
The above balance is how Flex works. You can specify the overall composition of a GUI
in Flex using an XML format. You can then implement components in ActionScript which
176
can be referenced in the XML. This isn't perfect, but at my current level of insight into
GUI technologies, I feel this approach is most optimal I have seen.
In my opinion AngularJS has positioned itself a step closer to the declarative end point
than Flex. Yes, you can implement more coarse grained directives in AngularJS so you
end up with an HTML template that is similar to the above. But that was never the selling
point of AngularJS. It is usually the more fine grained directives that "Angularistas"
praise.
After realizing that AngularJS was not what I was looking for, I actually started
implementing my own GUI toolkit inspired partly Flex, and based on JavaScript and
jQuery. I will get back to that later in this article.
Another example of how the templating mechanism is intrusive is when you look at how
many function calls that can (and will) be embedded in the HTML. Function calls on
the $scope object. Before AngularJS it was "best practice" to keep function calls out of
the HTML. For instance, you should not use the onclick event attributes on HTML
elements, but rather attach event listeners via JavaScript. Somehow that was forgotten
with AngularJS, and now we are back to embedding JavaScript function calls in the
HTML.
behaviour are different semantic areas of an application. These semantics are what
separates the designer's work from the developer's - not syntax or tools.
There is a significant difference between specifying the looks and behaviour of an
application, even if the syntax used for the two tasks is the same.
179
The two-way data binding in AngularJS kind of gets in the way of creating this data flow.
You will end up binding your form fields to model variables that are separate from your
model data objects, to avoid polluting your data objects. That way you copy data from a
model object into form field model properties, further into form fields, then back into form
field model properties, then send them to the server, and if that succeeds, you can
update your original model data object. Here is the data flow you will end up with in
AngularJS:
180
While you can get an app to work with this data flow, it kind of takes the glamour away
from two-way data binding.
Critique Summary
We do indeed need better frameworks to make it easier to develop larger HTML /
JavaScript applications, but I don't feel that AngularJS has chosen the right approach.
We do need to find a balance between the declarative HTML and the imperative
JavaScript, but I feel that AngularJS has gone too far in the declarative direction. We do
need to find ways to bind data to HTML elements / GUI components, but the one-way
data binding in the AngularJS directives and two-way data binding in forms can be done
better. But this is just my opinion. The whole purpose of this article was to help you form
your own opinion.
An Alternative
As mentioned earlier I have been looking a long time for the "perfect" GUI toolkit, and
when AngularJS turned out not to be what I was looking for either, I finally decided to
make an attempt at a GUI toolkit myself.
181
The GUI toolkit is inspired by Flex and based on jQuery. The toolkit is centered around a
page controller which scans the HTML page for elements referencing GUI components.
For each GUI component reference found, a corresponding JavaScript object is created.
These objects (components) are then kept internally in the page controller.
The page controller helps you avoid the "jQuery soup" which often occurs in larger
jQuery apps. The page controller helps you break the code into components and
modules, and keep references to instantiated components and modules etc.
The component approach avoids the DOM-centric code you can easily end up with in
jQuery powered web apps, especially if they use jQuery plugins as GUI components.
Therefore the toolkit does not use jQuery plugins for its GUI components.
The toolkit is called jqComponents (as in jQuery Components) and can be found
at jqcomponents.com
Whether this approach will be closer to the "perfect" I am looking for than AngularJS is
too early to say. Only time will tell. The initial proof-of-concept looks promising though
(but I am biased of course).
182