Question
How does data binding work in AngularJS?
It is reasonably clear how AngularJS propagates changes from a view to a model. However, how does AngularJS detect changes to model properties without JavaScript property getters and setters?
For example, how does AngularJS know that this assignment occurred and update a bound view?
myobject.myproperty = "new value";
I have read about JavaScript watchers, but some older browser features are not supported in Internet Explorer 6 and Internet Explorer 7. How does AngularJS detect model changes in those environments?
Short Answer
AngularJS 1.x uses dirty checking, not property setters, getters, or browser-level property-change events. It repeatedly evaluates registered watch expressions during a digest cycle and compares each current value with its previously recorded value. When a value differs, AngularJS updates bindings and runs relevant listeners. Changes made through AngularJS-managed events automatically start a digest; changes from external code usually require $scope.$apply() or an AngularJS wrapper such as $timeout.
Concept
AngularJS data binding is built around three closely related ideas:
- A model: values stored on a scope, such as
user.name. - A watcher: a function that reads a value and remembers its previous result.
- A digest cycle: the process in which AngularJS runs watchers and looks for changed values.
For a template binding such as:
<p>{{ user.name }}</p>
AngularJS creates a watcher that can evaluate user.name. During a digest, it evaluates that expression, compares the result with the previously stored result, and updates the DOM if the value changed.
AngularJS 1.x does not need setters or getters for ordinary data binding. It does not intercept this assignment:
$scope.user.name = "Ada";
Instead, at the next digest, AngularJS evaluates $scope.user.name again and sees that it no longer matches its old value. This comparison-based approach is called dirty checking.
A digest normally begins after work AngularJS knows about, including:
- AngularJS directives such as
ng-clickandng-model - AngularJS services such as , , and
Mental Model
Imagine AngularJS as an inspector making regular rounds through a checklist.
Each watcher is one item on the checklist:
“What is the current value of
user.name?”
On the previous round, the inspector recorded "Grace". On the next round, it sees "Ada". Because the value is different, AngularJS knows something changed and refreshes the parts of the page that display it.
The assignment itself does not notify AngularJS:
$scope.user.name = "Ada";
AngularJS notices only when it performs its next inspection round: the digest cycle.
$apply() is like telling the inspector, “External code has finished changing things; please make a round now.”
Syntax and Examples
Use scope properties in templates, and let AngularJS start digests from its directives and services.
<div ng-app="app" ng-controller="ProfileController">
<p>Hello, {{ user.name }}!</p>
<button ng-click="rename()">Rename</button>
</div>
angular.module("app", [])
.controller("ProfileController", function ($scope) {
$scope.user = {
name: "Grace"
};
$scope.rename = function () {
$scope.user.name = "Ada";
};
});
When the button is clicked:
- AngularJS runs
rename()becauseng-clickbelongs to AngularJS. - The function changes
$scope.user.name.
Step by Step Execution
Consider this controller and template:
<p>{{ account.status }}</p>
<button ng-click="activate()">Activate</button>
$scope.account = { status: "Pending" };
$scope.activate = function () {
$scope.account.status = "Active";
};
Here is what happens step by step:
- AngularJS links the template to the scope.
- It registers a watcher for the expression
account.status. - During the first digest, the watcher evaluates to
"Pending". - AngularJS stores
"Pending"as the last known value and renders it in the paragraph. - The user clicks the button.
ng-clickcallsactivate().activate()assigns"Active"to$scope.account.status.
Real World Use Cases
AngularJS dirty checking and digest scheduling appear in common application tasks:
- Forms:
ng-modelcopies user input into scope data, then bound validation messages and previews refresh. - HTTP requests:
$http.get()receives server data, updates a controller model, and AngularJS refreshes the view. - Timers:
$timeout()changes a countdown or notification state and automatically triggers a digest. - Third-party widgets: A map, payment widget, WebSocket client, or legacy jQuery plugin may invoke callbacks outside AngularJS. Wrap model updates in
$apply()or use$timeout(). - Live dashboards: New metrics update scope values; bindings redraw text, classes, and repeated rows.
- Validation rules: A watcher can derive one field from another, although computed values are often clearer when calculated directly in controller methods or services.
Example with a WebSocket-style external callback:
socket.onmessage = function (event) {
var message = JSON.parse(event.data);
$scope.$apply(function () {
$scope.latestMessage = message.text;
});
};
Without $apply(), the value may change in memory but the AngularJS view may not refresh until some later AngularJS event happens.
Real Codebase Usage
In production AngularJS code, developers generally avoid manually triggering digests unless integrating external code.
Prefer AngularJS-aware services
Use $timeout rather than the browser's setTimeout when the callback changes scope data:
$timeout(function () {
$scope.message = "Saved";
}, 500);
$timeout schedules AngularJS-aware work, so the view updates automatically.
Integrate external callbacks carefully
For callbacks from non-AngularJS libraries, call $apply() around the model update:
mapWidget.onMarkerSelected(function (marker) {
$scope.$apply(function () {
$scope.selectedMarker = marker;
});
});
If a digest may already be running, $evalAsync() is often safer for scheduling scope work:
mapWidget.onMarkerSelected(function (marker) {
$scope.$evalAsync( () {
$scope. = marker;
});
});
Common Mistakes
Expecting a plain assignment to update the view immediately
This external callback changes the model, but AngularJS may not know to run a digest:
setTimeout(function () {
$scope.status = "Complete";
}, 1000);
Use $timeout instead:
$timeout(function () {
$scope.status = "Complete";
}, 1000);
Or use $apply() when you must use an external callback:
setTimeout(function () {
$scope.$apply(function () {
$scope.status = "Complete";
});
}, 1000);
Calling $apply() inside ng-click
This is incorrect because AngularJS is already processing an AngularJS event:
$scope. = () {
$scope.$apply( () {
$scope. = ;
});
};
Comparisons
| Approach | How changes are detected | Typical AngularJS use |
|---|---|---|
| Dirty checking | AngularJS reevaluates watched expressions and compares old and new values | Standard AngularJS 1.x binding |
| Getters and setters | A property assignment runs custom code immediately | Not required for AngularJS scopes |
| Browser property observation | The browser notifies code when a property changes | Not the basis of AngularJS 1.x binding; older browser support was limited |
$watch | Registers a scope expression to compare during digests | Reacting to a particular model value in JavaScript |
$watchCollection | Shallowly observes array items or object properties | Lists and small collections where items may be added or removed |
Deep $watch(..., true) | Compares nested object data |
Cheat Sheet
- AngularJS 1.x binding uses dirty checking.
- A watcher evaluates an expression and compares its current value with its previous value.
- A digest cycle runs watchers until values stabilize.
- AngularJS does not need property setters/getters to notice normal model changes.
ng-click,ng-model,$http,$timeout, and$intervaltrigger AngularJS-aware updates.- External callbacks do not necessarily trigger a digest; use
$apply()or$evalAsync().
// Register a watcher
$scope.$watch("user.name", function (newValue, oldValue) {
// Respond to a changed value
});
// External callback integration
externalApi.onChange(function (value) {
$scope.$apply(function () {
$scope.value = value;
});
});
// AngularJS-aware delay
$timeout(function () {
$scope.value = "updated";
}, 1000);
Rules:
FAQ
Does AngularJS use getters and setters for data binding?
No. AngularJS 1.x primarily uses dirty checking: it reevaluates watch expressions during digest cycles and compares their values.
What triggers an AngularJS digest cycle?
AngularJS directives and services commonly trigger it, including ng-click, ng-model, $http, $timeout, and $interval. External library callbacks may require $apply().
Why does my AngularJS view not update after setTimeout?
The browser's setTimeout is outside AngularJS. Use $timeout, or wrap the scope update in $scope.$apply().
What is the difference between $apply() and $digest()?
$apply() evaluates code and starts a root-level digest. $digest() checks only the current scope and its descendants. Application code generally uses $apply() only for external integration.
Does detect nested object changes automatically?
Mini Project
Description
Build a small AngularJS status panel that updates a displayed connection state. It demonstrates the difference between an AngularJS-managed update and an update received from an external callback.
Goal
Display a connection status and safely refresh it when an external status event arrives.
Requirements
- Create a controller with an initial connection status of
Disconnected. - Display the status using an AngularJS template binding.
- Add a button that changes the status through
ng-click. - Simulate an external status callback that changes the status after a delay.
- Ensure the external update is visible in the view.
Keep learning
Related questions
Abort Ajax Requests with jQuery jqXHR.abort()
Learn how to cancel an in-progress jQuery Ajax request with jqXHR.abort(), handle abort status safely, and avoid stale UI updates.
Access the Correct this Inside a JavaScript Callback
Learn why JavaScript this changes in callbacks and how to preserve an object context using bind, arrow functions, and event handler patterns.
Add Key-Value Pairs to JavaScript Objects
Learn how to add key-value pairs to JavaScript objects with dot and bracket notation, dynamic keys, examples, and common mistakes.