为什么这个Angular ui路由器代码在$ digest中导致无限循环?

时间:2022-09-25 08:45:59

I've boiled the code down as much as I can. Something about nested states and the event handling/broadcasting is causing an infinite loop. In Chrome I can pause it and see that it is looping forever in Angular's $digest function. Any idea why? Is it a bug in my example code, or a bug in Angular, or the UI Router?

我尽可能地把代码煮熟了。关于嵌套状态和事件处理/广播的东西导致无限循环。在Chrome中我可以暂停它并看到它在Angular的$ digest函数中永远循环。知道为什么吗?这是我的示例代码中的错误,还是Angular或UI路由器中的错误?

<!doctype html>
<html ng-app='bugapp' ng-controller='BugAppCtrl'>
<head>
  <script src='//code.jquery.com/jquery-1.10.1.min.js'></script>

  <!-- Angular 1.2.11 -->
  <script src='//ajax.googleapis.com/ajax/libs/angularjs/1.2.11/angular.js'></script>

  <!-- UI router 0.2.8 -->
  <script src='//cdn.jsdelivr.net/angular.ui-router/0.2.8/angular-ui-router.js'></script>

  <script>
angular.module('bugapp', ['ui.router'])
  .run(function ($rootScope, $state, $stateParams) {
    $rootScope.$state = $state;
    $rootScope.$stateParams = $stateParams;
  })
  .config(function ($locationProvider, $stateProvider, $urlRouterProvider) {
    $locationProvider.html5Mode(false);

    $stateProvider
      .state("root", {
        abstract: true,
        url: "/servletContext?asUser",
        template: '<div ui-view></div>'  // ???
      })
      .state("root.home", {
        abstract: true,
        url: "/home",
        template: "<div ng-if='hasData()' ui-view ></div>"

      })
      .state("root.home.profile", {
        url: "/profile",
        template: '<div>whatever</div>'
      })

  })
  .controller('BugAppCtrl', function ($scope, $state, $stateParams, $log, $location) {
    $log.log('BugAppCtrl: constructor');

    $scope.hasData = function() {
      var res = !!$scope.foo;
      // $log.log("hasData called, returing " + res + " foo is " + $scope.foo);
      return res;
    };

    $scope.$on('$stateChangeSuccess', function () {
      $log.log("State changed! (to " + $state.current.name + ")");
      $scope.foo = 'junk';
      $scope.$broadcast("resetfoo");
    });

    $state.go('root.home.profile');
  });
  </script>
</head>
<body>
  <div ui-view></div>
</body>
</html>

1 个解决方案

#1


5  

I suspect this is a bug in the UI Router, for two reasons:

我怀疑这是UI路由器中的一个错误,原因有两个:

  1. I tried your code and then tried downgrading the version of UI Router to 0.2.7. When I used 0.2.7, it worked.
  2. 我尝试了你的代码,然后尝试将UI路由器的版本降级到0.2.7。当我使用0.2.7时,它起作用了。

  3. Even if you keep using version 0.2.8 of UI Router, if you perform the state change through $location instead of $state, it works. Here's an example of using $location instead of the $state.go call:

    即使您继续使用UI路由器的0.2.8版本,如果您通过$ location而不是$ state执行状态更改,它也可以。这是使用$ location而不是$ state.go调用的示例:

    $location.path('/servletContext/home/profile');
    

Although I use and recommend UI Router (can't do without nested views), if you find in the future that you want to do any sort of intercepting or redirecting when the user tries to go to certain pages, I recommend using $location.path instead of $state, for reasons I described in a blog post

虽然我使用并推荐UI路由器(不能没有嵌套视图),但如果您在将来发现当用户尝试访问某些页面时您想要进行任何类型的拦截或重定向,我建议使用$ location。由于我在博客文章中描述的原因,而不是$ state

Edit: I haven't tried with parameters before but just tried with the code you posted (I created a 2nd controller and assigned it to your 'root.home.profile' state), and it works. Instructions from UI Router are here. But basically if you set your URL in your state definition the same way you would with UI Router:

编辑:我之前没有尝试过参数,只是尝试使用你发布的代码(我创建了第二个控制器并将其分配给你的'root.home.profile'状态),它的工作原理。 UI路由器的说明在这里。但基本上,如果您在状态定义中设置URL的方式与使用UI路由器的方式相同:

url: "/profile/:foo",

then in your $location.path call add the parameter in the path:

然后在$ location.path调用中添加路径中的参数:

$location.path('/servletContext/home/profile/12');

and you can access the 12 in the controller from

你可以从控制器访问12

$stateParams.foo

#1


5  

I suspect this is a bug in the UI Router, for two reasons:

我怀疑这是UI路由器中的一个错误,原因有两个:

  1. I tried your code and then tried downgrading the version of UI Router to 0.2.7. When I used 0.2.7, it worked.
  2. 我尝试了你的代码,然后尝试将UI路由器的版本降级到0.2.7。当我使用0.2.7时,它起作用了。

  3. Even if you keep using version 0.2.8 of UI Router, if you perform the state change through $location instead of $state, it works. Here's an example of using $location instead of the $state.go call:

    即使您继续使用UI路由器的0.2.8版本,如果您通过$ location而不是$ state执行状态更改,它也可以。这是使用$ location而不是$ state.go调用的示例:

    $location.path('/servletContext/home/profile');
    

Although I use and recommend UI Router (can't do without nested views), if you find in the future that you want to do any sort of intercepting or redirecting when the user tries to go to certain pages, I recommend using $location.path instead of $state, for reasons I described in a blog post

虽然我使用并推荐UI路由器(不能没有嵌套视图),但如果您在将来发现当用户尝试访问某些页面时您想要进行任何类型的拦截或重定向,我建议使用$ location。由于我在博客文章中描述的原因,而不是$ state

Edit: I haven't tried with parameters before but just tried with the code you posted (I created a 2nd controller and assigned it to your 'root.home.profile' state), and it works. Instructions from UI Router are here. But basically if you set your URL in your state definition the same way you would with UI Router:

编辑:我之前没有尝试过参数,只是尝试使用你发布的代码(我创建了第二个控制器并将其分配给你的'root.home.profile'状态),它的工作原理。 UI路由器的说明在这里。但基本上,如果您在状态定义中设置URL的方式与使用UI路由器的方式相同:

url: "/profile/:foo",

then in your $location.path call add the parameter in the path:

然后在$ location.path调用中添加路径中的参数:

$location.path('/servletContext/home/profile/12');

and you can access the 12 in the controller from

你可以从控制器访问12

$stateParams.foo