Unit testing
Option 1: Using permissions makes mocking dependencies in control unit tests very simple. In the first version:
$routeProvider .when('/friends', { templateUrl: 'views/friends.html', controller: 'FriendListCtrl', resolve: { friendList: ['DataService', function(DataService) { return DataService.getFriendList(); }] } }) angular.module('myApp') .controller('FriendListCtrl', ['$scope', 'friendList', function($scope, friendList) { $scope.friends = friendList; }]);
Since friendList
is injected into the controller, mocking it in the test is as simple as passing a simple object to the $controller
service:
var friendListMock = [ // ... ]; $controller('FriendListCtrl', { $scope: scope, friendList: friendListMock })
Option 2: You cannot do this with the second option, and you will have to spy on / stub the DataService. Since data requests in the second embodiment are immediately called up when the controller is created, testing will be very confusing as soon as you start to execute several, conditional or dependent (more about this later) data requests.
View Initialization
Option 1: Allows you to prevent initialization of the view until all permissions have been executed. This means that everything expected to wait for data (including directives) will have it immediately.
Option 2: If data requests are made in the controller, the view will be displayed, but will not have any data until the requests are completed (which will be at some unknown point in the future). This is akin to a flash of unrelated content and can be harsh, but it can work.
Real difficulties arise when you have components in your view that are waiting for data and not providing it because they are still being retrieved. Then you will have to hack this, forcing each of your components to wait or delay initialization for some unknown amount of time, or to have $watch
an arbitrary variable for them before initialization. Very dirty.
Prefers to allow
While you can load source data in controllers, solutions already do this in a much cleaner and more declarative way.
The ngRoute resolver, however, lacks several key features, the most notable of which is the dependent solution. What if you want to provide 2 data items to your controller: the customer and information about their regular store? This is not easy with ngRoute:
resolve: { customer: function($routeParams, CustomerService) { return CustomerService.get($routeParams.customerId); }, usualStore: function(StoreService) { // can't access 'customer' object here, so can't get their usual store var storeId = ...; return StoreService.get(storeId); } }
You can hack this by loading the usualStore
from the controller after entering customer
, but why bother when it can be done purely in ui-router with dependent permissions:
resolve: { customer: function($stateParams, CustomerService) { return CustomerService.get($stateParams.customerId); }, usualStore: function(StoreService, customer) {