最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Angular Run Block - Use UI-Router $stateProvider to Resolve Promise - Stack Overflow

programmeradmin1浏览0评论

UI-Router is different than Angular's ngRoute. It supports everything the normal ngRoute can do as well as many extra functions.

I am changing my Angular app from ngRoute to UI-Router. But I cannot quite figure out how to inject resolve function programmatically - the piece of code that I use outside Controller and config.

So, with standard Angular's ngRoute I can dynamically inject my resolve promise in the Angular run block:

app.run(function ($route) {
  var route = $route.routes['/'];
  route.resolve = route.resolve || {};
  route.resolve.getData = function(myService){return myService.getSomeData();};
});

Now how do I inject resolve promise in a similar way using UI-Router? I tried passing $stateProvider to get access to states but that was failing for me.

angular.module('uiRouterSample').run(
  [          '$rootScope', '$state', '$stateProvider'
    function ($rootScope,   $state, $stateProvider) {

      //$stateProvider would fail

UI-Router is different than Angular's ngRoute. It supports everything the normal ngRoute can do as well as many extra functions.

I am changing my Angular app from ngRoute to UI-Router. But I cannot quite figure out how to inject resolve function programmatically - the piece of code that I use outside Controller and config.

So, with standard Angular's ngRoute I can dynamically inject my resolve promise in the Angular run block:

app.run(function ($route) {
  var route = $route.routes['/'];
  route.resolve = route.resolve || {};
  route.resolve.getData = function(myService){return myService.getSomeData();};
});

Now how do I inject resolve promise in a similar way using UI-Router? I tried passing $stateProvider to get access to states but that was failing for me.

angular.module('uiRouterSample').run(
  [          '$rootScope', '$state', '$stateProvider'
    function ($rootScope,   $state, $stateProvider) {

      //$stateProvider would fail
Share Improve this question edited Nov 19, 2015 at 16:05 Vad asked Nov 16, 2015 at 15:59 VadVad 3,7409 gold badges51 silver badges82 bronze badges 1
  • Can you share the logic of how you are currently deciding what should be resolved for a given route? I think you can take that logic and extract it into a set of UI Router states that will achieve the same result. For example, you can use state parameters in the resolve functions to make them dynamic. – Sunil D. Commented Nov 19, 2015 at 20:00
Add a ment  | 

4 Answers 4

Reset to default 6 +25

You can use resolve to provide your controller with data before it loads the next state. To access the resolved objects, you will need to inject them into the controller as dependencies.

Let's use a shopping list application as an example. We'll start by defining our application module, and including ui.router as a dependency.:

angular.module('myApp', ['ui.router']);

We now want to define the module that will be specific to the shopping list page of our application. We'll define a shoppingList module, include the states for that module, a resolve for that state, and the controller.

Shopping List Module

angular.module('myApp.shoppingList').config(function ($stateProvider) {

    $stateProvider.state('app.shoppingList', {
        url: '/shopping-list',
        templateUrl: 'shopping-list.html',
        controller: 'ShoppingListController',
        resolve: {
            shoppingLists: function (ShoppingListService) {
                return ShoppingListService.getAll();
            }
        }
    });

});

We now can inject our resolved objects into our controller as dependencies. In the above state, I am resolving an object to the name shoppingLists. If I want to use this object in my controller, I include it as a dependency with the same name.

Shopping List Controller

angular.module('myApp.shoppingList').controller('ShoppingListController', function ($scope, shoppingLists) {
    $scope.shoppingLists = shoppingLists;
});

For additional details read the Angular-UI Wiki, which includes an in-depth guide to using resolve.

Check the documentation:

Resolve

You can use resolve to provide your controller with content or data that is custom to the state. resolve is an optional map of dependencies which should be injected into the controller.

If any of these dependencies are promises, they will be resolved and converted to a value before the controller is instantiated and the $stateChangeSuccess event is fired.

The resolve property is a map object. The map object contains key/value pairs of:

  • key – {string}: a name of a dependency to be injected into the controller.
  • factory - {string|function}:
    • If string, then it is an alias for a service.
    • Otherwise if function, then it is injected and the return value is treated as the dependency. If the result is a promise, it is resolved before the controller is instantiated and its value is injected into the controller.

Examples:

Each of the objects in resolve below must be resolved (via deferred.resolve() if they are a promise) before the controller is instantiated. Notice how each resolve object is injected as a parameter into the controller.

example code for state

$stateProvider.state('myState', {
  resolve:{

     // Example using function with simple return value.
     // Since it's not a promise, it resolves immediately.
     simpleObj:  function(){
        return {value: 'simple!'};
     },

     // Example using function with returned promise.
     // This is the typical use case of resolve.
     // You need to inject any services that you are
     // using, e.g. $http in this example
     promiseObj:  function($http){
        // $http returns a promise for the url data
        return $http({method: 'GET', url: '/someUrl'});
     },

     // Another promise example. If you need to do some 
     // processing of the result, use .then, and your 
     // promise is chained in for free. This is another
     // typical use case of resolve.
     promiseObj2:  function($http){
        return $http({method: 'GET', url: '/someUrl'})
           .then (function (data) {
               return doSomeStuffFirst(data);
           });
     },        

     // Example using a service by name as string.
     // This would look for a 'translations' service
     // within the module and return it.
     // Note: The service could return a promise and
     // it would work just like the example above
     translations: "translations",

     // Example showing injection of service into
     // resolve function. Service then returns a
     // promise. Tip: Inject $stateParams to get
     // access to url parameters.
     translations2: function(translations, $stateParams){
         // Assume that getLang is a service method
         // that uses $http to fetch some translations.
         // Also assume our url was "/:lang/home".
         return translations.getLang($stateParams.lang);
     },

     // Example showing returning of custom made promise
     greeting: function($q, $timeout){
         var deferred = $q.defer();
         $timeout(function() {
             deferred.resolve('Hello!');
         }, 1000);
         return deferred.promise;
     }
  },

example controller, consuming the above resolve stuff

  // The controller waits for every one of the above items to be
  // pletely resolved before instantiation. For example, the
  // controller will not instantiate until promiseObj's promise has 
  // been resolved. Then those objects are injected into the controller
  // and available for use.  
  controller: function($scope, simpleObj, promiseObj, promiseObj2, translations, translations2, greeting){
      $scope.simple = simpleObj.value;

      // You can be sure that promiseObj is ready to use!
      $scope.items = promiseObj.data.items;
      $scope.items = promiseObj2.items;

      $scope.title = translations.getLang("english").title;
      $scope.title = translations2.title;

      $scope.greeting = greeting;
  }
})

You won't have access to $stateProvider in run and I don't think you should be changing the state configuration after creating it (and you might have no way of doing it anyway), why not just add resolve on state creation?

For adding resolve unconditionally to one or several states an abstract state should be used for inheritance:

$stateProvider
.state('root', {
    abstract: true,
    resolve: {
        mon: ...
    },
})
.state('some', {
    parent: 'root',
    ...
});

It is the preferred method which requires no hacking.

As for the equivalent of dynamic $route resolver in UI Router, here is a small problem. When a state is registered with state method, it is stored internally and prototypically inherited from the definition, rather than just being assigned to state storage.

Though the definition can be acquired later with $state.get('stateName'), it is not the same object as the one which is being used by the router internally. Due to how JS inheritance works, it won't make a difference if resolve object exists in the state, so new resolver properties can be added there. But if there's no $state.get('stateName').resolve, that's dead end.

The solution is to patch state method and add resolve object to all states, so resolver set can be modified later.

angular.module('ui.router.hacked', ['ui.router'])
.config(function ($stateProvider) {
  var stateOriginal = $stateProvider.state;
  $stateProvider.state = function (name, config) {
    config.resolve = config.resolve || {};
    return stateOriginal.apply(this, arguments);
  }
})

angular.module('app', ['ui.router.hacked']).run(function ($state) {
  var state = $state.get('some');
  state.resolve.someResolver = ...;
});

As any other hack, it may have pitfalls and tends to break. Though this one is very solid and simple, it requires additional unit-testing and shouldn't be used if conventional methods could be used instead.

发布评论

评论列表(0)

  1. 暂无评论
ok 不同模板 switch ($forum['model']) { /*case '0': include _include(APP_PATH . 'view/htm/read.htm'); break;*/ default: include _include(theme_load('read', $fid)); break; } } break; case '10': // 主题外链 / thread external link http_location(htmlspecialchars_decode(trim($thread['description']))); break; case '11': // 单页 / single page $attachlist = array(); $imagelist = array(); $thread['filelist'] = array(); $threadlist = NULL; $thread['files'] > 0 and list($attachlist, $imagelist, $thread['filelist']) = well_attach_find_by_tid($tid); $data = data_read_cache($tid); empty($data) and message(-1, lang('data_malformation')); $tidlist = $forum['threads'] ? page_find_by_fid($fid, $page, $pagesize) : NULL; if ($tidlist) { $tidarr = arrlist_values($tidlist, 'tid'); $threadlist = well_thread_find($tidarr, $pagesize); // 按之前tidlist排序 $threadlist = array2_sort_key($threadlist, $tidlist, 'tid'); } $allowpost = forum_access_user($fid, $gid, 'allowpost'); $allowupdate = forum_access_mod($fid, $gid, 'allowupdate'); $allowdelete = forum_access_mod($fid, $gid, 'allowdelete'); $access = array('allowpost' => $allowpost, 'allowupdate' => $allowupdate, 'allowdelete' => $allowdelete); $header['title'] = $thread['subject']; $header['mobile_link'] = $thread['url']; $header['keywords'] = $thread['keyword'] ? $thread['keyword'] : $thread['subject']; $header['description'] = $thread['description'] ? $thread['description'] : $thread['brief']; $_SESSION['fid'] = $fid; if ($ajax) { empty($conf['api_on']) and message(0, lang('closed')); $apilist['header'] = $header; $apilist['extra'] = $extra; $apilist['access'] = $access; $apilist['thread'] = well_thread_safe_info($thread); $apilist['thread_data'] = $data; $apilist['forum'] = $forum; $apilist['imagelist'] = $imagelist; $apilist['filelist'] = $thread['filelist']; $apilist['threadlist'] = $threadlist; message(0, $apilist); } else { include _include(theme_load('single_page', $fid)); } break; default: message(-1, lang('data_malformation')); break; } ?>