Thursday 27 November 2014

Resolve promises in router, defer controllers

Taken from this style guide:  http://toddmotto.com/opinionated-angular-js-styleguide-for-teams/

After creating services, we will likely inject them into a controller, call them and bind any new data that comes in. This becomes problematic of keeping controllers tidy and resolving the right data.

Thankfully, using angular-route.js (or a third party such as ui-router.js) we can use a resolve property to resolve the next view's promises before the page is served to us. This means our controllers are instantiated when all data is available, which means zero function calls.

Bad:
function MainCtrl (SomeService) {

var self = this;

// unresolved
self.something;

// resolved asynchronously
SomeService.doSomething().then(function (response) {
self.something = response;
});

}
angular
.module('app')
.controller('MainCtrl', MainCtrl);

Good:
function config ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
resolve: {
doSomething: function (SomeService) {
return SomeService.doSomething();
}
}
});
}
angular
.module('app')
.config(config);

At this point, our service will internally bind the response of the promise to another Object which we can reference in our "deferred-instantiated" controller:

Good:
function MainCtrl (SomeService) {
// resolved!
this.something = SomeService.something;
}
angular
.module('app')
.controller('MainCtrl', MainCtrl);

We can go one better, however and create a resolve property on our own Controllers to couple the resolves with the Controllers and avoid logic in the router.

Best:
// config with resolve pointing to relevant controller
function config ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl',
controllerAs: 'main',
resolve: MainCtrl.resolve
});
}
// controller as usual
function MainCtrl (SomeService) {
// resolved!
this.something = SomeService.something;
}
// create the resolved property
MainCtrl.resolve = {
doSomething: function (SomeService) {
return SomeService.doSomething();
}
};

angular
.module('app')
.controller('MainCtrl', MainCtrl)
.config(config);

Route changes and ajax spinners

While the routes are being resolved we want to show the user something to indicate progress. Angular will fire the $routeChangeStart event as we navigate away from the page, which we can show some form of loading and ajax spinner, which can then be removed on the $routeChangeSuccess event (see docs).

No comments:

Post a Comment