Порядковый порядок разрешения зависимостей в angular ui-router

Я установил контроллер верхнего уровня, который создается только тогда, когда обещание (возвращаемое Config factory) успешно разрешено. Это обещание в основном загружает конфигурацию веб-приложения с конечными точками RESTful и т.д.

$stateProvider
  .state('app', {
    url: '/',
    templateUrl: 'views/_index.html',
    controller: 'MainCtrl',
    resolve: {
      config: 'Config'
    }
  });

Эта настройка позволяет мне утверждать, что конфигурация должным образом загружена до того, как какой-либо более низкий контроллер получит возможность ее использовать.

Теперь мне нужно добавить в более глубокий вложенный контроллер еще один factory, который использует Config и работает только тогда, когда он разрешен (посмотрите на него как на упаковку $resource, которая нуждается в некоторых URL-адресах веб-сервисов). Если я это сделаю:

$stateProvider
  .state('app.bottom.page', {
    url: '/bottom/page',
    templateUrl: 'views/_a_view.html',
    controller: 'BottomLevelCtrl',
    resolve: {
      TheResource: 'MyConfigDependingResource'
    }
  });

похоже, что порядок оценки resolve не соответствует иерархии контроллера сверху вниз, а снизу вверх, поэтому:

  • app.bottom.page
  • ui-router пытается разрешить MyConfigDependingResource, но инъекция терпит неудачу, потому что Config никогда не инициализировался
  • Разрешение ui-router прекращается из-за ошибки (даже не выбрасывая Error s, но эта другая проблема), а Config никогда не инициализируется контроллером верхнего уровня

Почему ui-router разрешает зависимости в обратном порядке? Как я могу легко разрешить мой TheResource объект после, верхний уровень MainCtrl разрешил Config (не полагаясь на $inject, конечно)?

UPDATE: от этот журнал plnkr вы можете видеть, что верхний уровень resolve выполняется только после вложенный контроллер начал свой собственный процесс разрешения.

Ответы

Ответ 1

Аналогично ответу @Kasper Lewau, можно указать зависимость от разрешений, связанных с одним состоянием. Если одно из ваших разрешений зависит от одного или нескольких свойств разрешения от одного и того же блока разрешений. В моем случае checkS полагается на два других разрешений

.state('stateofstate', {
    url: "/anrapasd",
    templateUrl: "views/anrapasd.html",
    controller: 'SteofsteCtrl',
    resolve: {
        currU: function(gamMag) {
            return gamMag.checkWifi("jabadabadu")
        },
        userC: function(gamUser, $stateParams) {
            return gamUser.getBipi("oink")
        },
        checkS: ['currU', 'userC', 'gamMag', function(currU, userC, gamMag) {
            return gamMag.check(currU, userC);
        }]
    }
})

** PS: ** Подробнее о внутренней работе resolve см. раздел "Разрешает" следующий документ.

Ответ 2

Разрешить объекты, выполняемые параллельно, и поэтому не следует определенной иерархии. Однако вложенные разрешения возможны путем определения объекта решения более высокого порядка в качестве зависимости от вашего вторичного разрешения.

$stateProvider
  .state('topState', {
    url: '/',
    resolve: {
      mainResolveObj: ['$someService', function ($someService) {
        return 'I am needed elsewhere!';
      }]
    }
  })
  .state('topState.someOtherState', {
    url: '/some-other-place',
    resolve: {
      someOtherResolveObj: ['mainResolveObj', function (mainResolveObj) {
        console.log(mainResolveObj); //-> 'I am needed elsewhere!'. 
      }]
    }
  });

Какой-то плохой пример, но я понимаю, что вы понимаете суть этого. Определите имя объекта разрешения более высокого уровня как зависимость от вашего решения более низкого уровня, и он будет ждать его разрешения в первую очередь.

Вот как мы решили предварительно загрузить определенные данные перед объектами решения более низкого порядка, а также требования к аутентификации (среди прочего).

Удачи.

Ответ 3

Я согласен с вами в том, что решение должно быть целым, и я столкнулся со многими проблемами в этой области.

Однако они этого не делают, поэтому решение, которое я придумал, заключается в использовании моего собственного обещания, хранящегося в сервисе, который вы разрешаете, когда данные завершены. Я попытался изо всех сил изменить ваш plunkr на работу, но безрезультатно. Я больше не нажимаю на ваши ошибки, поэтому надеюсь, что вы сможете это сделать: http://plnkr.co/edit/Yi65ciQPu7lxjkFMBKLn?p=preview

Что он делает:

Сохраните конфигурацию в состоянии рядом с новым объектом обещания

myapp.service('state', function($q) {
  this.load = $q.defer();
  this.config = {}
})

При первом разрешении сохраните конфигурацию в этой службе и после того, как вы готовы решить новое обещание, которое мы создали выше.

myapp.factory('Config', function($http, $log, state) {
  return $http.get('example.json')
    .then(function (data) {
      angular.extend(state.config, data.data);
      state.load.resolve();
    });
});

Последний и самый важный шаг - не называть наш второй контент функции разрешения дочерних элементов до тех пор, пока не будет разрешено наше обещание выше:

myapp.factory('MyConfigDependingResource', function($log, state) {
  return state.load.promise.then(function() {
    if (!state.config.text) {
      $log.error('Config is uninitialized!');
    }
    else {
      // Do wonderful things
    }
    return
  });
});

Главное, что вам нужно знать, это то, что конфигурация теперь хранится в состоянии. Там должны быть пути вокруг этого, но я так и делал это раньше.

Ответ 4

Пока они не могут быть закованы в цепочку, вы можете звонить один из другого:

var resolve2 = ['$stateParams',function($stateParams){
  // do resolve 2 stuff
  ...
  return true;
}];

var resolve1 = ['$stateParams',function($stateParams){
  // do resolve 1 stuff
  ...
  // now do resolve2 stuff
  return $injector.invoke(resolve2,this,{$stateParams:$stateParams});
}];


$stateProvider.state("myState", {
  resolve: {
    resolve1: resolve1
  }
});