Chapter1:
To run the shopping cart example correctly, you need to change the ng-app attribute in HTML tag.
And now you've got you code like:
<html ng-app>
<head>
<title>Your Shopping Cart</title>
</head>
<body ng-controller='CartController'>
<h1>Your Order</h1>
<div ng-repeat='item in items'>
<span>{{item.title}}</span>
<input ng-model='item.quantity'>
<span>{{item.price | currency}}</span>
<span>{{item.price * item.quantity | currency}}</span>
<button ng-click="remove($index)">Remove</button>
</div>
<script src="../angular.js"></script>
<script>
function CartController($scope) {
$scope.items = [
{title: 'Paint pots', quantity: 8, price: 3.95},
{title: 'Polka dots', quantity: 17, price: 12.95},
{title: 'Pebbles', quantity: 5, price: 6.95}
];
$scope.remove = function(index) {
$scope.items.splice(index, 1);
}
}
</script>
</body>
</html>
Chapter2: Anatomy of an AngularJS Application
On page 16, the author discussed the two ways to display text in HTML: double-curly braces and ng-bind directive, and giving the cons of the former one.
"...with the double-curly syntax, on the very first page load of your application’s index.html, there’s a chance that your user will see the un-rendered template before Angular has a chance to replace the curlies with your data. Subsequent views won’t suffer from this.The reason is that the browser loads the HTML page, renders it, and only then does Angular get a chance to interpret it as you intended."
Form Inputs
In the section of 'Form Inputs', there are a sample program called 'calculator', and the author gave four versions of it to demonstrate the features of AngularJs, but all of these versions have their own problems. Since the author just provided the core code of the samples, you will have to make up the rest to complete the programs, making them able to run.
The 1st version: The basic idea here is how to keep track of user's input and bind that of the relevant string to be represented, with AngularJs. You can bind a model variable to a user input control with "ng-model" attribute, and hook user's events up with "ng-change" attribute. The snippet code at the top of page 17 wont work, since the variables needed and startingEstimate are attached to funding. Well, at the bottom of page 13, the author suggested that it'd be better to create a model object on $scope, and put all the data on it. But he didn't stick to his words. Anyway, in order to run the sample, you would have your code like:
<html ng-app>
<body>
<form ng-controller="StartUpController">
Starting: <input ng-change="computeNeeded()" ng-model="funding.startingEstimate">
Recommendation: {{funding.needed}}
</form>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js">
</script>
<script>
function StartUpController($scope) {
$scope.funding = { startingEstimate: 0, needed: 0 };
$scope.computeNeeded = function() {
$scope.funding.needed = $scope.funding.startingEstimate * 10;
};
}
</script>
</body>
</html>
The 2nd version: The whole idea is to demonstrate $watch. the drawback of the 1st version is that the value of needed will only be updated when the user changes the value in the input box, and it wont get updated if the value is changed in other ways. So the author suggested that we adopt $watch() in AngularJs. So you get your code like(page 17):
<html ng-app>
<body>
<form ng-controller="StartUpController">
Starting: <input ng-model="funding.startingEstimate">
Recommendation: {{funding.needed}}
</form>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js">
</script>
<script>
function StartUpController($scope) {
$scope.funding = { startingEstimate: 0, needed: 0 };
computeNeeded = function() {
$scope.funding.needed = $scope.funding.startingEstimate * 10;
};
$scope.$watch('funding.startingEstimate', computeNeeded);
}
</script>
</body>
</html>
You dont need to keep eyes on the change event of the input box now, hence you can remove the event binding. Beside from that, you move the function computeNeeded() to immediately under StartUpController class.
The 3rd version: now we are going to add a submit event binding of the form. Here again, the author put all the model data immediately under $scope. And further more, to have the UI initialized with default values, I added the declaration of the data (needed and startingEstimate) with initial values, so now your code should be like(page 18):
<html ng-app>
<body>
<form ng-submit="requestFunding()" ng-controller="StartUpController">
Starting: <input ng-change="computeNeeded()" ng-model="startingEstimate">
Recommendation: {{needed}}
<button>Fund my startup!</button>
</form>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js">
</script>
<script>
function StartUpController($scope) {
$scope.needed = 0;
$scope.startingEstimate = 0;
$scope.computeNeeded = function() {
$scope.needed = $scope.startingEstimate * 10;
};
$scope.requestFunding = function() {
window.alert("Sorry, please get more customers first.");
};
}
</script>
</body>
</html>
The 4th version: reset button now. Here I added default values to needed and startingEstimate. And if you just run the code, you may find clicking reset button only resets the input box, but not the text after 'Recommendation:', so I copied the logic of watching from version 2 to solve that. But there is still a problem, clicking the reset button will cause the handler of submit event requestFunding() be invoked, I dont know why(page 18).
<html ng-app>
<body>
<form ng-submit="requestFunding()" ng-controller="StartUpController">
Starting: <input ng-model="startingEstimate">
Recommendation: {{needed}}
<button>Fund my startup!</button>
<button ng-click="reset()">Reset</button>
</form>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js">
</script>
<script>
function StartUpController($scope) {
$scope.needed = 0;
$scope.startingEstimate = 0;
$scope.requestFunding = function() {
window.alert("Sorry, please get more customers first.");
};
$scope.reset = function() {
$scope.startingEstimate = 0;
};
computeNeeded = function() {
$scope.needed = $scope.startingEstimate * 10;
};
$scope.$watch('startingEstimate', computeNeeded);
}
</script>
</body>
</html>
It is worth noticing that the HTML markup modified by AngularJs is:
<form ng-submit="requestFunding()" ng-controller="StartUpController" class="ng-scope ng-pristine ng-valid ng-binding">
Starting: <input ng-model="startingEstimate" class="ng-pristine ng-valid">
Recommendation: 0
<button>Fund my startup!</button>
<button ng-click="reset()">Reset</button>
</form>
Lists, Tables, and Other Repeated Elements
You might notice the code: <ul ng-controller=''> on page 22, I have to say the author was not so thorough when writing this book. So here is the correct code of the student roster program:
<html ng-app>
<body ng-controller='StudentListController'>
<ul>
<li ng-repeat='student in students'>
<a href='/student/view/{{student.id}}'>{{student.name}}</a>
</li>
</ul>
<button ng-click="insertTom()">Insert</button>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js">
</script>
<script>
var students = [{name:'Mary Contrary', id:'1'},
{name:'Jack Sprat', id:'2'},
{name:'Jill Hill', id:'3'}];
function StudentListController($scope) {
$scope.students = students;
$scope.insertTom = function () {
$scope.students.splice(1, 0, {name:'Tom Thumb', id:'4'});
};
}
</script>
</body>
</html>
And the album sample, giving an idea of how to make use of $index, $first, $middel, $last (page 22):
<html ng-app>
<body>
<table ng-controller='AlbumController'>
<tr ng-repeat='track in album'>
<td>{{$index + 1}}</td>
<td>{{track.name}}</td>
<td>{{track.duration}}</td>
</tr>
</table>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js">
</script>
<script>
var album = [{name:'Southwest Serenade', duration: '2:34'},
{name:'Northern Light Waltz', duration: '3:21'},
{name:'Eastern Tango', duration: '17:45'}];
function AlbumController($scope) {
$scope.album = album;
}
</script>
</body>
</html>
Hiding and Showing
CSS Classes and Styles
Notification sample: how to use "ng-class" and "ng-style"(page 25).
<html ng-app>
<style>
.error {
background-color: red;
}
.warning {
background-color: yellow;
}
</style>
<body>
<div ng-controller='HeaderController'>
...
<div ng-class='{error: isError, warning: isWarning}'>{{messageText}}</div>
...
<button ng-click='showError()'>Simulate Error</button>
<button ng-click='showWarning()'>Simulate Warning</button>
</div>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js">
</script>
<script>
function HeaderController($scope) {
$scope.isError = false;
$scope.isWarning = false;
$scope.showError = function() {
$scope.messageText = 'This is an error!';
$scope.isError = true;
$scope.isWarning = false;
};
$scope.showWarning = function() {
$scope.messageText = 'Just a warning. Please carry on.';
$scope.isWarning = true;
$scope.isError = false;
};
}
</script>
</body>
</html>
Restaurant sample(page 26):
<html ng-app>
<style>
.selected {
background-color: lightgreen;
}
</style>
<body>
<table ng-controller='RestaurantTableController'>
<tr ng-repeat='restaurant in directory' ng-click='selectRestaurant($index)' ng-class='{selected: $index==selectedRow}'>
<td>{{restaurant.name}}</td>
<td>{{restaurant.cuisine}}</td>
</tr>
</table>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js">
</script>
<script>
function RestaurantTableController($scope) {
$scope.directory = [{name:'The Handsome Heifer', cuisine:'BBQ'},
{name:'Green\'s Green Greens', cuisine:'Salads'},
{name:'House of Fine Fish', cuisine:'Seafood'}];
$scope.selectRestaurant = function(row) {
$scope.selectedRow = row;
};
}
</script>
</body>
</html>
There are two points to highlight in section: Separating UI Responsibilities with Controllers
- There is another way in AngularJs to associate a controller to a DOM node except ng-controller directive, which is applicable to dynamically loaded DOM through view/route;
- Controller can be nested, and finally you end up with hierarchical controllers, and the $scope of which are chained up by inheritance.
The three versions of shopping cart sample:
The 1st version: in the function $scope.subtotal(), the reference to discount is wrong, the correct code is(page 30):
<html ng-app>
<body>
<div ng-controller="CartController">
<div ng-repeat="item in items">
<span>{{item.title}}</span>
<input ng-model="item.quantity">
<span>{{item.price | currency}}</span>
<span>{{item.price * item.quantity | currency}}</span>
</div>
<div>Total: {{totalCart() | currency}}</div>
<div>Discount: {{bill.discount | currency}}</div>
<div>Subtotal: {{subtotal() | currency}}</div>
</div>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js">
</script>
<script>
function CartController($scope) {
$scope.bill = {};
$scope.items = [
{title: 'Paint pots', quantity: 8, price: 3.95},
{title: 'Polka dots', quantity: 17, price: 12.95},
{title: 'Pebbles', quantity: 5, price: 6.95}
];
$scope.totalCart = function() {
var total = 0;
for (var i = 0, len = $scope.items.length; i < len; i++) {
total = total + $scope.items[i].price * $scope.items[i].quantity;
}
return total;
}
$scope.subtotal = function() {
return $scope.totalCart() - $scope.bill.discount;
};
function calculateDiscount(newValue, oldValue, scope) {
$scope.bill.discount = newValue > 100 ? 10 : 0;
}
$scope.$watch($scope.totalCart, calculateDiscount);
}
</script>
</body>
</html>
The 2nd version: The idea is how to simplify the watching mechanism, keep an eye on a outcome of a complex calculation? Or just simple objects. In addition, there is an extension in Chrome for analyzing that. there is a variable reference issue in the line(page 32):<div>Total: {{bill.total | currency}}</div>
<html ng-app>
<body>
<div ng-controller="CartController">
<div ng-repeat="item in items">
<span>{{item.title}}</span>
<input ng-model="item.quantity">
<span>{{item.price | currency}}</span>
<span>{{item.price * item.quantity | currency}}</span>
</div>
<div>Total: {{bill.totalCart | currency}}</div>
<div>Discount: {{bill.discount | currency}}</div>
<div>Subtotal: {{bill.subtotal | currency}}</div>
</div>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js">
</script>
<script>
function CartController($scope) {
$scope.bill = {};
$scope.items = [
{title: 'Paint pots', quantity: 8, price: 3.95},
{title: 'Polka dots', quantity: 17, price: 12.95},
{title: 'Pebbles', quantity: 5, price: 6.95}
];
var calculateTotals = function() {
var total = 0;
for (var i = 0, len = $scope.items.length; i < len; i++) {
total = total + $scope.items[i].price * $scope.items[i].quantity;
}
$scope.bill.totalCart = total;
$scope.bill.discount = total > 100 ? 10 : 0;
$scope.bill.subtotal = total - $scope.bill.discount;
};
$scope.$watch('items', calculateTotals, true);
}
</script>
</body>
</html>
The code in the 3rd version from the book works fine, no need to change anything, the strategy is to just watch a result of a function(page 32-33).