Part 1 covered the fundamentals. Now let's go deeper into the parts that actually make or break a real AngularJS application: filters done right, the services abstraction, and understanding the digest cycle.
Filters
Filters format data for display without modifying the underlying model. They can be used in templates or injected into controllers and services.
Built-in Filters
<!-- Currency -->
{{ price | currency:'$' }} <!-- $1,234.56 -->
<!-- Date -->
{{ timestamp | date:'dd MMM yyyy' }} <!-- 30 Jan 2016 -->
<!-- LimitTo -->
{{ items | limitTo:5 }} <!-- first 5 items -->
<!-- OrderBy -->
<li ng-repeat="u in users | orderBy:'name'">{{u.name}}</li>
<!-- Filter (search) -->
<li ng-repeat="u in users | filter:searchText">{{u.name}}</li>
Custom Filter
app.filter('truncate', function() {
return function(text, length) {
if (!text) return '';
return text.length > length ? text.substring(0, length) + '...' : text;
};
});
{{ article.body | truncate:120 }}
Services, Factories & Providers
All three are singletons โ created once, shared across the app. The difference is in how you define them.
Factory โ functional style
app.factory('ItemService', function() {
var items = [];
return {
list: function() { return items; },
add: function(item) { items.push(item); }
};
});
Service โ class style
app.service('ItemService', function() {
var items = [];
this.list = function() { return items; };
this.add = function(item) { items.push(item); };
});
Provider โ configurable before app starts
app.provider('Config', function() {
var debug = false;
this.setDebug = function(val) { debug = val; };
this.$get = function() {
return { isDebug: function() { return debug; } };
};
});
// In config phase โ before app runs
app.config(function(ConfigProvider) {
ConfigProvider.setDebug(true);
});
The Digest Cycle
Understanding the digest cycle is essential for avoiding mysterious bugs and performance issues in AngularJS.
AngularJS tracks model changes through dirty checking. On each digest cycle, it compares the current value of every watched expression to its previous value. If anything changed, the cycle runs again โ until nothing changes (or until the limit is hit and it throws an error).
Key rules
- Keep
$watchcallbacks fast โ they run on every digest - Avoid expensive computations in filters applied to large arrays
- Use
$scope.$apply()when integrating with non-Angular async code (setTimeout, jQuery events) - Prefer
$scope.$digest()over$scope.$apply()when you know the scope boundary
// Triggering digest from outside Angular (e.g. a jQuery callback)
element.on('click', function() {
$scope.$apply(function() {
$scope.someModel = 'updated';
});
});
Part 3 will cover custom directives in depth, compile vs link functions, and transclusion.