Angularjs 角度视图在http拦截器之后不更新

Angularjs 角度视图在http拦截器之后不更新,angularjs,angularjs-scope,angular-ui-router,angular-services,angular-http-interceptors,Angularjs,Angularjs Scope,Angular Ui Router,Angular Services,Angular Http Interceptors,我正在控制器中使用promise在$http请求后呈现视图。除了http拦截器在401错误期间介入之外,其他一切都正常工作。当这种情况发生时,这就是我面临的问题: http拦截器从api中获取401错误,补充道 被拒绝的查询将被缓冲并显示登录模式 登录成功后,排队的api查询 再次尝试401 before,api返回请求的数据 成功 然而,即使从api返回数据, 角度视图不会更新。视图仅在更新后更新 手动刷新该状态 这些是我的代码: scope.$on('event:auth-loginRequ

我正在控制器中使用promise在
$http
请求后呈现视图。除了http拦截器在401错误期间介入之外,其他一切都正常工作。当这种情况发生时,这就是我面临的问题:

  • http拦截器从api中获取401错误,补充道 被拒绝的查询将被缓冲并显示登录模式

  • 登录成功后,排队的api查询 再次尝试401 before,api返回请求的数据 成功

  • 然而,即使从api返回数据, 角度视图不会更新。视图仅在更新后更新 手动刷新该状态

  • 这些是我的代码:

    scope.$on('event:auth-loginRequired', function() {
        LoginModalService.openLoginModal();
        console.log('login needed');
    });
    scope.$on('event:auth-loginCancelled', function() {
            $state.go('index');
    });
    
    1)http侦听器:

    scope.$on('event:auth-loginRequired', function() {
        LoginModalService.openLoginModal();
        console.log('login needed');
    });
    scope.$on('event:auth-loginCancelled', function() {
            $state.go('index');
    });
    
    我在这里使用的是
    httpauth拦截器

    $httpProvider.interceptors.push(['$rootScope', '$q', 'httpBuffer', function($rootScope, $q, httpBuffer) {
              return {
                responseError: function(rejection) {
                  if (!rejection.config.ignoreAuthModule) {
    
                      var rejectionReasons = ['token_not_provided', 'token_expired', 'token_absent', 'token_invalid'];
    
                      angular.forEach(rejectionReasons, function(value, key) {
    
                           if(rejection.data.error === value) {
                             var deferred = $q.defer();
                             httpBuffer.append(rejection.config, deferred);
                             $rootScope.$broadcast('event:auth-loginRequired', rejection);
                             return deferred.promise;
                           }
                      })
    
                  }
                  // otherwise, default behaviour
                  return $q.reject(rejection);
                }
              };
            }]);
    
    2)http侦听器监视:

    scope.$on('event:auth-loginRequired', function() {
        LoginModalService.openLoginModal();
        console.log('login needed');
    });
    scope.$on('event:auth-loginCancelled', function() {
            $state.go('index');
    });
    
    3)控制器:

    .controller('FetchData', ['$scope', 'DataService', 
                function($scope, DataService) {
    
                DataService.fetchNamesList()
                .success(function(names) {
                    $scope.namesList = names;
                }).error(function(error) {
                // The login modal should show if auth error occurs
                });         
    
    }])         
    
    这里,
    DataService.fetchNamesList()
    从api发出get请求

    如果针对上述内容触发了身份验证拦截器,我可以看到在成功登录后再次尝试
    $http$\u GET
    ,但
    $scope.namesList
    未在视图中更新

    到目前为止我的想法:

    scope.$on('event:auth-loginRequired', function() {
        LoginModalService.openLoginModal();
        console.log('login needed');
    });
    scope.$on('event:auth-loginCancelled', function() {
            $state.go('index');
    });
    
    1) 我正在考虑为上述内容添加一个额外的
    $watch
    ,以便它在http拦截器之后工作。我想到了这一点,然后我放弃了这个选项,因为如果我在一个页面中有多个get请求(如仪表板),那么观察每个请求可能会过火

    2) 登录后手动刷新ui路由器状态,如下所示。这是可行的,但当视图涉及表单提交时,完成的表单将在状态重新加载后重置,这没有帮助:

    $state.go($state.current, {}, {reload: true});
    
    所以我确定如何解决这个问题。有人能带我到这里吗

    编辑: 在@ChrisFoster在评论中提出建议后,我将
    return deferred.promise
    移至
    angular.forEach
    之外,并略微更改了forEach检查。这似乎可以在登录后正确更新视图。但是,现在模式会显示每个错误,而不仅仅是拒绝原因中列出的错误:

      $httpProvider.interceptors.push(['$rootScope', '$q', 'httpBuffer', function($rootScope, $q, httpBuffer) {
          return {
            responseError: function(rejection) {
              if (!rejection.config.ignoreAuthModule) {
    
                  var rejectionReasons = ['token_not_provided', 'token_expired', 'token_absent', 'token_invalid'];
    
                  // Loop through each rejection reason and redirect
                  angular.forEach(rejectionReasons, function(value, key) {
    
                       if(!(rejection.data.error === value)) {
                          return $q.reject(rejection);
                       }
    
                  });
    
                  var deferred = $q.defer();
                  httpBuffer.append(rejection.config, deferred);
                  $rootScope.$broadcast('event:auth-loginRequired', rejection);
                  return deferred.promise;
    
              }
              // otherwise, default behaviour
              return $q.reject(rejection);
            }
          };
        }]);
    

    这就是我目前正在使用的有效方法:

    1) 删除了
    angular.forEach
    ,并将其替换为简单的
    if
    语句。因为我只检查了几个条件,
    if
    是我认为的一个不错的选择

    2) 我在
    if
    语句中添加了
    deferred.promise
    ,之后一切正常。该视图在登录后得到更新,控制器可以继续解析从api返回的数据

    这是我的代码:

      $httpProvider.interceptors.push(['$rootScope', '$q', 'httpBuffer',
          function($rootScope, $q, httpBuffer) {
            return {
              responseError: function(rejection) {
                if (!rejection.config.ignoreAuthModule) {
    
                    var rejectionReasons = ['token_not_provided', 'token_expired', 'token_absent', 'token_invalid'];
    
                    // Loop through each rejection reason and redirect
                    if (
                          rejection.data.error === 'token_not_provided' ||
                          rejection.data.error === 'token_expired' ||
                          rejection.data.error === 'token_absent' ||
                          rejection.data.error === 'token_invalid'
    
                        )
                    {
                      var deferred = $q.defer();
                      httpBuffer.append(rejection.config, deferred);
                      $rootScope.$broadcast('event:auth-loginRequired', rejection);
                      return deferred.promise;
                    }
    
                }
                // otherwise, default behaviour
                return $q.reject(rejection);
              }
            };
          }]);
    
    感谢@ChrisFoster指出了错误:

    您的返回延迟。承诺在angular.forEach中。这意味着 它返回到函数内部,而不是承诺处理程序。 您需要将其分配给forEach之外的变量 函数并以这种方式返回


    很高兴看到你的答案有效。如果愿意,您仍然可以使用
    angular.forEach
    ,下面是一个示例:

    $httpProvider.interceptors.push(['$rootScope', '$q', 'httpBuffer', function($rootScope, $q, httpBuffer) {
      return {
        responseError: function(rejection) {
    
          var loginRequired = false;
          var rejectionReasons = [
            'token_not_provided', 'token_expired',
            'token_absent', 'token_invalid'
          ];
    
          // Loop through each rejection reason
          angular.forEach(rejectionReasons, function(value, key) {
            if (!(rejection.data.error === value)) {
              // Set loginRequired, and check this later
              // We *can't* return here because we are inside a function :)
              loginRequired = true;
            }
          });
    
          // Create a promise
          var deferred = $q.defer();
    
          if (loginRequired && !rejection.config.ignoreAuthModule) {
            // Display a login module and queue a retry request
            httpBuffer.append(rejection.config, deferred);
            $rootScope.$broadcast('event:auth-loginRequired', rejection);
          } else {
            // Not a auth error, or ignoreAuthModule is enabled
            // Therefore immediately reject the promise with the fail value
            deferred.reject(rejection);
          }
    
          // Return the promise
          return deferred.promise;
        }
      };
    }]);
    
    forEach
    出现问题的原因是您将函数作为参数传递给了
    forEach
    。当您
    return
    在其中时,您将在
    forEach
    函数中返回该值,而不是在HTTP侦听器函数中返回该值。因此,您有三种选择:

  • 您可以使用传统的javascript
    进行
    循环
  • 您可以使用
    if
    条件语句
  • 您可以使用跟踪的“外部”变量

  • 您的示例使用的是第二种方法,但是如果您仍然想使用
    angular.forEach
    my code上面是第三种方法的演示。第一种方法也是可能的,但不太常见,也有点混乱。

    您的
    返回延迟。promise
    位于
    angular.forEach
    中。这意味着它将返回到该函数内部,而不是承诺处理程序。您需要将其分配给
    forEach
    函数之外的变量,并以这种方式返回。我不确定这是否是你的全部问题,但这是问题的一部分:)@ChrisFoster我想你是对的。当我在
    forEach
    循环之外删除
    return deferred.promise
    时,视图会很好地更新。所以你发现了我的错误。然而,我仍然停留在如何修复它的权利。我的意思是,我稍微更改了foreach,这样它就可以检查不相等,如果不继续使用
    deliver.promise
    ,就会返回默认行为。但是,当我将
    deferred.promise
    移动到foreach外部时,模式显示为每400个错误,而不是我在RejectionReasures下列出的错误。延迟和承诺的事情让我很困惑,我不知道是非。我用错误的尝试更新了我的问题。你能帮我吗?仍然坚持下去:(@ChrisFoster我终于让它工作了。我必须阅读基础知识,花一些时间学习
    promise
    angular forEach
    。后来我意识到我错了。我现在使用了一个简单的
    if
    语句并处理了
    延迟。promise
    。这似乎有效。我补充了这一点作为答案由于这是我目前正在使用的解决方案。如果您觉得这可以更有效地完成,请务必告诉我。非常感谢您的评论。您50字的评论让我获得了丰富的知识。:)很高兴你能让它工作!我很高兴你能弄清楚到底发生了什么。你目前的答案非常好,但如果你还想知道如何使用
    angular.forEach
    ,请查看我在下面添加的答案:)完美。这有助于成堆的sinc