Using Resolve In AngularJS Routes

http://odetocode.com/blogs/scott/archive/2014/05/20/using-resolve-in-angularjs-routes.aspx

In a previous post about testing I mentioned that route resolves can make authoring unit tests for a controller easier. Resolves can also help the user experience.

A resolve is a property you can attach to a route in both ngRoute and the more robust UI router. A resolve contains one or more promises that must resolve successfully before the route will change. This means you can wait for data to become available before showing a view, and simplify the initialization of the model inside a controller because the initial data is given to the controller instead of the controller needing to go out and fetch the data.

As an example, let’s use the following simple service which uses $q to simulate the async work required to fetch some data.

app.factory( "messageService" , function ($q){
     return {
         getMessage: function (){
             return $q.when( "Hello World!" );
         }
     };
});

And now the routing configuration that will use the service in a resolve.

$routeProvider
     .when( "/news" , {
         templateUrl: "newsView.html" ,
         controller: "newsController" ,
         resolve: {
             message: function (messageService){
                 return messageService.getMessage();
         }
     }
})

Resolve is a property on the routing configuration, and each property on resolve can be an injectable function (meaning it can ask for service dependencies). The function should return a promise.

When the promise completes successfully, the resolve property (message in this scenario) is available to inject into a controller function. In other words, all a controller needs to do to grab data gathered during resolve is to ask for the data using the same name as the resolve property (message).

app.controller( "newsController" , function (message) {
     $scope.message = message;
});

You can work with multiple resolve properties. As an example, let’s introduce a 2nd service. Unlike the messageService, this service is a little bit slow.

app.factory( "greetingService" , function ($q, $timeout){
    return {
        getGreeting: function (){
            var deferred = $q.defer();
            $timeout( function (){
                deferred.resolve( "Allo!" );
            },2000);
            return deferred.promise;
        }
    }
});

Now the resolve in the routing configuration has two promise producing functions.

.when( "/news" , {
     templateUrl: "newsView.html" ,
     controller: "newsController" ,
     resolve: {
         message: function (messageService){
             return messageService.getMessage();
         },
         greeting: function (greetingService){
             return greetingService.getGreeting();
         }
     }
})

And the associated controller can ask for both message and greeting.

app.controller( "newsController" , function ($scope, message, greeting) {
     $scope.message = message;
     $scope.greeting = greeting;
});

Composing Resolve

Although there are benefits to moving code out of a controller, there are also drawbacks to having code inside the route definitions. For controllers that require a complicated setup I like to use a small service dedicated to providing resolve features for a controller. The service relies heavily on promise composition and might look like the following.

app.factory( "newsControllerInitialData" , function (messageService, greetingService, $q) {
     return function () {
         var message = messageService.getMessage();
         var greeting = greetingService.getGreeting();
 
         return $q.all([message, greeting]).then( function (results){
             return {
                 message: results[0],
                 greeting: results[1]
             };
         });
     }
});

Not only is the code inside a service easier to test than the code inside a route definition, but the route definitions are also easier to read.

.when( "/news" , {
     templateUrl: "newsView.html" ,
     controller: "newsController" ,
     resolve: {
         initialData: function (newsControllerInitialData){
             return newsControllerInitialData();
         }
     }
})

And the controller is also easy.

app.controller( "newsController" , function ($scope, initialData) {
     $scope.message = initialData.message;
     $scope.greeting = initialData.greeting;
});

One of the keys to all of this working is $q.all, which is a beautiful way to compose promises and run requests in parallel.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值