Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this Angular ui router code cause an infinite loop in $digest?

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?

<!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>
like image 307
Rob N Avatar asked Feb 07 '14 15:02

Rob N


1 Answers

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

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

    $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

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:

url: "/profile/:foo",

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

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

and you can access the 12 in the controller from

$stateParams.foo
like image 176
Matt Metlis Avatar answered Nov 08 '22 11:11

Matt Metlis