Как перезагрузить асинхронные данные в область действия контроллера при обновлении маршрута?

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

В настоящее время я использую аргумент разрешения в определении состояния для вызова функции.

.state('app.theState', {
  url: '/theRoute/:theRouteId',
  views: {
      'menuContent': {
          templateUrl: 'templates/theRoute.html',
          controller: 'TheRouteCtrl'
      }
  },
  resolve: {theResolvedResult: function(theService){
             return theService.theAsyncCall().then(function(result){
                return result;
              },function(errMsg){
                return errMsg;
              });}
})

Данные передаются в контроллер в качестве аргумента.

.controller( 'TheRouteCtrl', function($scope, $state, $log, theResolvedResult)

Я обновляю контроллер $scope в часах на глобальном уровне, который содержит ResolvedResult. (используя обходной путь, описанный в конце этого post). Я пробовал следить за самим спором, но он так и не сработал.

$scope.$state=$state;
$scope.$watch('$state.$current.locals.globals.theResolvedResult', function (result) {
  $scope.aValue = result.aValue;
});

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

ionic.bundle.js:26794 TypeError: Cannot read property 'aValue' of undefined
at route_ctrl.js:234

Как я могу исправить эти ошибки, или есть лучший способ сделать это?


person Johanna Belanger    schedule 27.04.2017    source источник


Ответы (3)


Возможно, просто остерегайтесь случая, когда result не определено:

$scope.$watch('$state.$current.locals.globals.theResolvedResult', function (result) {
  if (result) {
    $scope.aValue = result.aValue;
  }
});
person mikwat    schedule 28.04.2017
comment
Спасибо, это хороший прагматичный обходной путь. Я все еще хотел бы получить решение, которое запускает часы только для текущего контроллера, чтобы исключить расточительную проверку, а также не скрывать ошибки, когда результат должен быть определен, а не является. Так что пока оставлю вопрос открытым. - person Johanna Belanger; 29.04.2017

Критическое изменение для Angular V1.6

Переместите логику инициализации в функцию $onInit

/* REPLACE
$scope.$state=$state;
$scope.$watch('$state.$current.locals.globals.theResolvedResult', function (result) {
  $scope.aValue = result.aValue;
});
*/

//WITH
this.$onInit = function() {
    $scope.aValue = $state.$current.locals.globals.theResolvedResult.aValue;
};

Логика инициализации, основанная на наличии привязок, должна быть помещена в метод $onInit() контроллера, который гарантированно будет всегда вызываться после назначения привязок.

— Руководство по переходу на AngularJS (версия 1.6 $compile Breaking Change bcd0d4)



Обработчик ошибочного отклонения

Решение заключается в преобразовании отклоненного обещания в выполненное обещание:

/* ERRONEOUS
resolve: {theResolvedResult: function(theService){
         return theService.theAsyncCall().then(function(result){
            return result;
          },function(errMsg){
            //ERRONEOUS converts rejection
            return errMsg;
          });}
 */

// BETTER
resolve: {
    theResolvedResult: function(theService){
         return theService.theAsyncCall().then(function(result){
            console.log(result);
            return result;
         },function catchHandler(errMsg){
            console.log(errMsg);
            //return errMsg;
            //IMPORTANT re-throw error
            throw errMsg;
         });
     }
 

Важно повторно выдать ошибку в обработчике catch. В противном случае служба $q преобразует отклоненное обещание в выполненное обещание. Это похоже на то, как попытается ...оператор catch работает в ванильном JavaScript.

Метод $watch принимает функцию в качестве первого аргумент:

/* HACKY
$scope.$state=$state;
$scope.$watch('$state.$current.locals.globals.theResolvedResult', function (result) {
  $scope.aValue = result.aValue;
});
*/

//BETTER
//$scope.$state=$state;
//$scope.$watch('$state.$current.locals.globals.theResolvedResult',
$scope.$watch(
  function () {
    return $state.$current.locals.globals.theResolvedResult;
},
  function (result) {
    if (result) {
        $scope.aValue = result.aValue;
    };
});

Когда первый аргумент метода $watch равен строка, он оценивает ее как выражение Angular, используя $scope в качестве контекста. Поскольку служба $state не является свойством $scope, вместо нее следует использовать функцию.

person georgeawg    schedule 28.04.2017
comment
Спасибо за полезный совет! - person Johanna Belanger; 29.04.2017

Лучше всего вообще избегать взлома $watch. Проверка правильности значения должна происходить в сервисе или, по крайней мере, в файле resolve.

Проверьте этот плункер для демонстрации того, как должен работать resolve.

Общая идея состоит в том, что resolve имеет одну работу; чтобы убедиться, что данные готовы, когда мы этого захотим. В документах есть несколько примеров того, как ui.router обрабатывает resolve разрешения. Они немного ограничены, но обычно показывают, что ожидаются «хорошие» результаты. Обработка ошибок в конечном итоге остается на ваше усмотрение.

Наслаждаться!

person jrose    schedule 10.07.2017