You can display and update text anywhere in your UI using the ng-bind directive. It has two equivalent forms. One we’ve seen with double-curly braces:
<p>{{greeting}}</p>
Then there’s an attribute-based directive called ng-bind:
<p ng-bind="greeting"></p>
Both are equivalent in their output. If the model variable greeting is set to “Hi there,”
Angular will generate the HTML:
<p>Hi there</p>
Templates and Data Binding | 15
And the browser will display “Hi there”.
So why would you use one form over the other? We created the double-curly interpo‐
lation syntax to read more naturally and require less typing. While both forms produce equivalent output, 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.
The good news is that you can still use {{ }} in the majority of your templates. For the data binding you do in your index.html page, however, use ng-bind instead. That way, your users will see nothing until the data has loaded.
Form Inputs
Working with form elements in Angular is simple. As we’ve seen in several examples, you can use the ng-model attribute to bind elements to your model properties. This works with all the standard form elements like text inputs, radio buttons, checkboxes, and so on. We can bind a checkbox to a property like so:
<form ng-controller="SomeController">
<input type="checkbox" ng-model="youCheckedIt">
</form>
This means that:
1. When the user checks the box, a property called youCheckedIt on the SomeCon troller’s $scope will become true. Unchecking the box makes youCheckedIt false.
2. If you set $scope.youCheckedIt to true in SomeController, the box becomes checked in the UI. Setting it to false unchecks the box.
Now let’s say we actually want to take action when the user does something. For input elements, you use the ng-change attribute to specify a controller method that should be called whenever the user changes the input’s value. Let’s do a simple calculator to help startup owners understand how much money they need to get going:
<form ng-controller="StartUpController">
Starting: <input ng-change="computeNeeded()"
ng-model="funding.startingEstimate">
Recommendation: {{funding.needed}}
</form>
For our simplistic example, let’s just set the output to be ten times the user’s estimate.
We’ll also set a default value of zero to start:
function StartUpController($scope) { $scope.funding = { startingEstimate: 0 };
$scope.computeNeeded = function() {
$scope.needed = $scope.startingEstimate * 10;
};
}
There is, however, a potential problem with the strategy in the preceding code. The issue is that we’re only recomputing the needed amount when users type in the input field.
This works fine if this input field is only ever updated when users type in this particular input. But what if other inputs bind to this property in the model? What if it gets updated when data comes in from the server?
To update the field no matter how it gets updated, we want to use a $scope function called $watch(). We’ll talk about watch in detail later in this chapter. The basics are that you can call $watch() with an expression to observe and a callback that gets invoked whenever that expression changes.
In this case, we want to watch funding.startingEstimate and call computeNeeded() whenever it changes. We could then rewrite the StartUpController to use this tech‐
nique:
function StartUpController($scope) { $scope.funding = { startingEstimate: 0 };
computeNeeded = function() {
$scope.funding.needed = $scope.funding.startingEstimate * 10;
};
$scope.$watch('funding.startingEstimate', computeNeeded);
}
Note that the expression to watch is in quotes. Yes, it is a string. This string is evaluated as something called an Angular expression. Expressions can do simple operations and have access to the properties in the $scope object. We’ll cover expressions more later in this chapter.
You could also watch the return value of a function, but it won’t work to watch the property funding.startingEstimate as this evaluates to zero, its initial value, and that zero never changes.
Then, because our funding.needed will automatically update whenever funding.star tingEstimate changes, we can write a simpler template, like so:
<form ng-controller="StartUpController">
Starting: <input ng-model="funding.startingEstimate">
Recommendation: {{funding.needed}}
</form>
Templates and Data Binding | 17
There are some cases where you don’t want to take action on every change; instead, you want to wait until the user tells you he’s ready. Examples might be completing a purchase or sending a chat message.
If you have a form that groups inputs, you can use the ng-submit directive on the form itself to specify a function to call when the form submits. We can extend our previous example to let the user request funding for her startup by clicking a button:
<form ng-submit="requestFunding()" ng-controller="StartUpController">
Starting: <input ng-change="computeNeeded()" ng-model="startingEstimate">
Recommendation: {{needed}}
<button>Fund my startup!</button>
</form>
function StartUpController($scope) { $scope.computeNeeded = function() {
$scope.needed = $scope.startingEstimate * 10;
};
$scope.requestFunding = function() {
window.alert("Sorry, please get more customers first.");
};
}
The ng-submit directive also automatically prevents the browser from doing its default POST action when it tries to submit the form.
To handle other event cases, like when you want to provide interactions that don’t submit a form, Angular provides event-handling directives that resemble the browser’s native event attributes. For onclick, you’d use ng-click. For ondblclick, use ng-dblclick, and so on.
We can try this out by extending our startup calculator one last time with a reset button that will reset the input value to zero:
<form ng-submit="requestFunding()" ng-controller="StartUpController">
Starting: <input ng-change="computeNeeded()" ng-model="startingEstimate">
Recommendation: {{needed}}
<button>Fund my startup!</button>
<button ng-click="reset()">Reset</button>
</form>
function StartUpController($scope) { $scope.computeNeeded = function() {
$scope.needed = $scope.startingEstimate * 10;
};
$scope.requestFunding = function() {
window.alert("Sorry, please get more customers first.");
};
$scope.reset = function() {
$scope.startingEstimate = 0;
};
}