News

5 Angular tips from the experts

Get your hands on a collection of tips to take your code to the next level

ExpertAngular

Get your hands on a collection of tips to take your code to the next level

ExpertAngular

01. Dependency Injection annotation syntax

Dependency Injection (DI) in Angular is fairly simple, we just pass the dependency names that we want to the function as arguments, and Angular injects them in for us to use.

Behind the scenes Angular converts our functions to a string and then it reads any dependencies that we’re requesting. Note that at this point the arguments cannot be renamed.

When we minify our code (and we should), the argument names will be minified and Angular won’t be able to interpret them correctly, so we need to explicitly tell Angular what dependencies we’re requesting, and in what order we need them.

There are several ways to inject our dependencies to keep them safe when minified, for example by making use of the inline $inject syntax or array syntax. With the array syntax, we will need to pass the dependencies into our callback as an array, which will then hold the function as a final argument.

angular
.module('app', [])
.controller('MainCtrl', ['$scope', '$rootScope', function MainCtrl($scope, $rootScope) {
//
}]);

With the $inject syntax, we can create a property on the function itself, which will offer us more flexibility if we want to make the code slightly less callback-looking and enhance readability.

function MainCtrl($scope, $rootScope) {
//
}
MainCtrl.$inject = ['$scope', '$rootScope'];
angular
.module('app', [])
.controller('MainCtrl', MainCtrl);//
}]);

We can alternatively use a mix of both if preferred (using the array syntax but also passing a function in).

function MainCtrl($scope, $rootScope) {
//
}
angular
.module('app', [])
.controller('MainCtrl', ['$scope', '$rootScope', MainCtrl]);

Use ng-annotate to automate both of these syntaxes so we can save a lot of time typing, it’s clever enough to know whether to use $inject or the array syntax. You can install ng-annotate via NPM, point it to your project files, let it compile and automatically add the dependency injection annotations for you.

02. One-time binding

Angular 1.3 introduced a brilliant feature called one-time binding. Using one-time binding is good as it removes $$watchers from the $digest cycle after they’ve been parsed and populated.

If the data is a one-time static render then it can be bound once and removed from further $digest loops, this will help to keep future loops much lighter and therefore faster for the JavaScript to look up any change. This feature will also enable us to remove the value from Angular $digest cycle (which contains all our data to check states when any model values change) as soon as it becomes anything other than ‘undefined’.

The $digest cycle can become heavy and slow down our app, which isn’t very helpful if we only need to render our data once, for example with a dynamically populated navigation, static list content or view titles. To specify that an expression only needs to be bound once, we can prefix the expression with ‘::’, like ‘{{ ::name }}’.

03. Link functions for DOM

It may be extremely tempting to litter your Controllers with DOM manipulation, especially when integrating things such as plugins or third-party scripts and you need to set or get values.

Directives are a perfect way to encapsulate any necessary presentational logic, as they include a Controller, and offer a gateway to the DOM through the ‘link’ function. This ‘link’ function gives us the root element for the directive, so that we can actually access any element inside it and bind things such as ontouchstart listeners for example, which currently aren’t part of the Angular core.

When using a Controller alongside a directive, Angular passes it to the ‘link’ function as a fourth argument, so we can run any presentational logic from callbacks to raw DOM manipulation.

Remember that inside raw DOM listeners we will need to run $scope.$apply to tell Angular to look at any new values. As an example (excluding most directive properties) it is possible to pass in a Controller, aliased $ctrl to access presentational methods inside our raw DOM event listeners.

return {
..
link: function postLink($scope, $element, $attrs, $ctrl) {
// some code to fetch files from a made up drag/drop uploader
var drop = $element.find('.drop-zone')[0];
function onDrop(e) {
if (e.dataTransfer && e.dataTransfer.files) {
// assumes “uploadFiles” method handles the upload!
$ctrl.uploadFiles(e.dataTransfer.files);
// force a $digest cycle
$scope.$apply();
}
}
// events
drop.addEventListener('drop', onDrop, false);
} .. }

04. Unbind $rootScope.$on listeners

You’ll likely find the need at some stage during your Angular career to use the internal events system, $emit, $broadcast and $on. These event methods are available in both the $rootScope and $scope.

If we change views in our application, Angular will destroy our Controller and thus it will destroy the $scope object associated with it. This is great, however, all $scope objects are child objects that will inherit from the $rootScope, and this will then persist throughout the entire application.

We will need to manually unbind $rootScope listeners by calling the returned closure function when the $scope is destroyed by listening to the $destroy event, otherwise when we revisit a view where the same Controller is used, the $rootScope.$on will be bound again. This can then cause duplicated events and also some issues with the data syncing.

var unbind = $rootScope.$on(‘fooEvent’, function () {});
$scope.$on(‘$destroy’, unbind);

With multiple events, we tend to use an array and loop through to automatically unbind.

[
$rootScope.$on(‘fooEvent’ function () {}),
$rootScope.$on('barEvent, function () {})
].forEach(function (unbind) {
$scope.$on('$destroy', unbind);
});

Avoid DOM Filtering

Using a filter in the HTML of Angular is great, but can impact performance. Filters can be easily added using a pipe inside an expression like so:

{{ someDateValue | filter:date }}

That’s an example of using Angular’s built-in date filter, we can however write our own filters. Filters are fairly common on ng-repeat declarations however, and can be huge performance killers.

Filters run on every $digest loop and will create a new array every time that it runs. Watch for changes to a specific model (such as a user typing) or a user interaction (such as a user clicking a button) and then run a filter inside the Controller’s JavaScript using the $filter method, which will manually run your own filter upon detection of any change.


Get more expert Angular tips from Web Designer

×