$scope 变量不在 $rootScope.$on 内更新

$scope variable not update within $rootScope.$on

这显示编号 1 和一个 更改 按钮。当我单击该按钮时,它应该触发 $state.go,并且在控制器内,它应该检测 $rootScope.$on 中的事件并将数字更新为 2。但是,出于某种原因,即使使用 $digest 或 $apply,我也无法获取要更新的号码 (data.id)。

http://plnkr.co/edit/3qHnIm?p=preview

HTML

<!DOCTYPE html>
<html ng-app="myapp">

  <head>
        <script data-require="angular.js@*" data-semver="1.4.0-beta.5" src="https://code.angularjs.org/1.4.0-beta.5/angular.js"></script>
    <script data-require="ui-router@*" data-semver="0.2.13" src="//rawgit.com/angular-ui/ui-router/0.2.13/release/angular-ui-router.js"></script>

    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <title></title>
    <meta name="description" content="" />
    <meta name="viewport" content="width=device-width" />
  </head>

  <body class="container">
    <div ui-view=""></div>
    <div ng-controller="state2Ctrl">
      <a href="#" ng-click="change()">Change</a>
    </div>
    <script src="app.js"></script>
    <script src="controllers.js"></script>
  </body>

</html>

state1.html

<p>{{ data.id }}</p>

JS

var app = angular.module('myapp', ['ui.router']);

app.config(function($stateProvider, $urlRouterProvider) {

  $urlRouterProvider.otherwise('/state1/0');

  $stateProvider
    .state('state1', {
      url: '/state1/:id',
      controller: 'state1Ctrl',
      templateUrl: 'state1.html'
    });
});

app.controller('state2Ctrl', function ($scope, $rootScope, $state, $stateParams) {
  $scope.i = 1;

  $scope.change = function(){
    console.log('change state1');
    $state.go('state1', {id: $scope.i++});  
  };
});

app.controller('state1Ctrl', function ($scope, $rootScope, $state, $stateParams) {
  $scope.data = {id: 1};

  $rootScope.$on('$stateChangeSuccess', function(evt, toState, toParams, fromState, fromParams) {
    console.log($scope.data);

    $scope.$apply(function() {
      $scope.data = {id: 2};  
      console.log($scope.data);
    });
  });

});

谁能找出我的错误,为什么 data.id 没有在视图中得到更新?

更新 1

根据下面的 Radim 回复,我稍微更改了代码以更好地反映我的真实代码:http://plnkr.co/edit/yyg0Hrl5IgzP4e44aRet?p=preview

基本我的情况没有父状态。我想要做的是从 SAME 状态,用新参数触发该状态并更改一些 $scope 变量对象。就这些了。

JS

var i = 1;

app.controller('state1Ctrl', function($scope, $rootScope, $state, $stateParams) {
  $scope.change = function() {
    console.log('change state1: ' + i);
    $state.go('state1', {
      id: i++
    });
  };

  $scope.data = {
    id: 1
  };

  $rootScope.$on('$stateChangeSuccess', function(evt, toState, toParams, fromState, fromParams) {
    console.log('$stateChangeSuccess: ');
    console.log($scope.data);

    $scope.data = $scope.data || {};
    $scope.data.id = toParams.id;
    console.log('After assign:');
    console.log($scope.data);

  });

});

我点了4次Change后发现,$stateChangeSuccess个事件堆积起来如下图。如果您能帮助解释并避免这种情况,我也不确定为什么。

原因是,你每次都在重新创建data

// this will create new reference to data
// in current scope, while not effecting any other (parent)
$scope.data = {id: 2}; 

如果您想更新现有参考,我们必须这样做

// here we work with existing reference
// if it was created, it will be used, the id property will be updated
$scope.data = $scope.data  || {}; 
$scope.data.id = 2;

an updated version.

状态现在有了父级,我们将一直继承 $scope,所以我们可以将它用作 $scope.data

的共享持有者
  $stateProvider
    .state('state1Parent', {
      template: '<div ui-view=""></div>',
      controller: 'state1ParentCtrl',
    });
  $stateProvider
    .state('state1', {
      parent: 'state1Parent',
      url: '/state1/:id',
      controller: 'state1Ctrl',
      templateUrl: 'state1.html'
    });

控制器更新了 def:

app.controller('state1Ctrl', function ($scope, $rootScope, $state, $stateParams) {
  // no $scope.data definition, used the inherited
  //$scope.data = {id: 3}; 

  $rootScope.$on('$stateChangeSuccess', function(evt, toState, toParams, fromState, fromParams) {
    console.log($scope.data);

    // because this state change listener still have access to $scope
    // we can update the value of data.id
    // because it is in fact parent reference
    // se even next state will have access to it


    //$scope.$apply(function() {
      $scope.data = $scope.data || {};  
      $scope.data.id = toParams.id;  
      console.log($scope.data);
    //});
  });

});

还有一个父控制器,带有引用数据定义

app.controller('state1ParentCtrl', function ($scope, $rootScope, $state, $stateParams) {
  $scope.data = { id : 1 };
})

检查一下here

我并不是说我们应该走这条路。但试图说明问题是什么,以及如何解决

扩展:我们可以在没有父状态的情况下这样做吗?

来自扩展问题:

Basic I didn't have the situation with parent state in my case. What I want to do is from the SAME state, trigger that state with new params and change some $scope variable objects. That's all.

所以,这里有什么问题。

1) controller生命周期最短,在angular世界。它只会在状态期间存在。一旦我们离开它,导航到其他 - 它会消失(如果没有内存泄漏挂钩)

2)controller的作用域会被销毁,一旦controller被销毁...

3) 我们必须将各州视为孤立的岛屿。如果我们确实可以访问状态 $scope - 即使在内部声明 $rootScope.$on('$stateChangeSuccess' - 我们确实可以访问当前控制器的当前状态...的 $scope 。事实上,我们为内存泄漏

创建了一个候选对象
app.controller('state1Ctrl', ...
  ...
  $rootScope.$on('$stateChangeSuccess', ...
     // HERE - $scope belongs to Controller, and its life cycle is very short
     $scope.data =

4) 如果我们想改变一些值,在 "SAME state" 的中间 - 在转换之后我们必须将它移出。它不能处于当前状态,因为当前 state/view/controller(及其范围)将不相同。

5) 保存数据的方式是 - 服务(单例)或父类内部的模型(将通过 view/scope 继承继承)

6) $rootScope.$on 应该在某些 .运行() 方法中声明 - 在控制器之外。但在这种情况下,它似乎并没有增加任何价值。因为如果我们将共享数据放入某个服务中......我们可以在任何地方进行,例如在其他控制器中也是如此