如何在路由刷新时将异步数据重新加载到控制器范围?

时间:2017-04-27 23:06:34

标签: angularjs angular-ui-router angular1.6

我有一个服务功能,每次访问或刷新路由时都需要调用它。该函数返回Angular承诺。每次调用函数时,promise的结果都需要加载到控制器的作用域中。

我目前正在使用状态定义的resolve参数来调用该函数。

.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)

我正在更新持有theResolvedResult的全局监视中的控制器$ scope。 (使用此workaround末尾所述的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

如何修复这些错误,还是有更好的方法来实现这一目标?

3 个答案:

答案 0 :(得分:1)

也许只是防范result未定义的情况:

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

答案 1 :(得分:0)

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 Migration Guide (V1.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 service会将被拒绝的承诺转换为已履行的承诺。这类似于try...catch statement在vanilla JavaScript中的工作方式。

$watch method将函数作为其第一个参数:

/* 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 method的第一个参数是一个字符串时,它使用$scope作为其上下文将其评估为Angular Expression。由于$state服务不是$scope的属性,因此应使用函数。

答案 2 :(得分:0)

最好的办法是首先避免$watch黑客攻击。确保价值良好应该在服务中发生,或者至少在resolve中发生。

查看this Plunker,了解resolve应如何运作的演示。

一般的想法是resolve有一份工作;在我们需要时确保数据准备就绪。 docs有一些关于ui.router如何处理resolve分辨率的示例。它们有点受限,但通常表明预期会有“好”的结果。错误处理最终由您自己选择。

享受!