在我的$ scope函数中看不到变量

时间:2014-06-11 10:14:10

标签: javascript angularjs

即使我看到变量在事件监听器中更新,但是当状态更改事件发生时,我没有更新到$scope函数的变量有问题。

以下是代码:

angular.module('testapp')
.controller('AnotherCtrl',
['$scope', '$rootScope', '$state',
    function ($scope, $rootScope, $state) {
        'use strict';
        console.log("Init prevState.");
        var prevState = 'null_state';
        $scope.state = prevState;


        $rootScope.$on('$stateChangeError',
            function (event, toState, toParams, fromState, fromParams, error) {
                console.log("Error");
                if (toState.name === 'anotherState') {

                    console.log("Old State:" + prevState);
                    prevState = fromState.name;
                    console.log("New State:" + prevState);


                }
            })

        $rootScope.$on('$stateChangeSuccess',
            function (event, toState, toParams, fromState, fromParams) {
                console.log("Success");
                if (toState.name === 'anotherState') {

                    console.log("Old State:" + prevState);
                    prevState = fromState.name;
                    console.log("New State:" + prevState);

                }
            })


        $scope.goBack = function () {
            //$scope.state = 'anotherState';
            console.log("goBack:" + prevState);
            $state.transitionTo(prevState, {arg: 'Robert'});
        };
    }]);

以下是HTML模板:

<div>
<h1>Another view</h1>
<br>
<br>
State:  <span ng-model="state">{{state}}</span>
<br>
<br>
<button ng-click="goBack()">Back</button>
</div>

这是控制台输出:

console output

因此,当我按下调用函数goBack()的按钮上的按钮时,prevState变量仍为'null_state'

任何人都可以解释问题是什么吗?

在审核答案后

更新 我已经审核并测试了所有建议的解决方案。核心问题AFAICS与字符串类型是不可变的无关。我认为最好的答案来自@Khanh TO。首先创建控制器时,事件侦听器未初始化。这是用bootstrapping(angular.module.run)修复的。每次加载状态/视图时,控制器都会重新初始化,导致变量prevState被重新初始化,因此监听器函数和goBack()函数使用不同的外部prevState变量。在rootScope上存储prevState解决了这个问题。

4 个答案:

答案 0 :(得分:1)

根本原因是无论何时更改状态,角度重新初始化与当前状态关联的控制器,导致先前存储的状态为已清除为-initialized

因此,解决方案是将先前状态存储在共享服务中。在这种情况下,我们可以使用$rootScope

$rootScope.$on('$stateChangeError',
            function (event, toState, toParams, fromState, fromParams, error) {
                console.log("Error");
                if (toState.name === 'anotherState') {
                    $rootScope.prevState = fromState.name;
                }
 });

 $rootScope.$on('$stateChangeSuccess',
            function (event, toState, toParams, fromState, fromParams) {
                console.log("Success");
                if (toState.name === 'anotherState') {
                    $rootScope.prevState = fromState.name;
                }
  });

  $scope.goBack = function () {
    $state.transitionTo($rootScope.prevState, {arg: 'Robert'});
    //or $state.transitionTo($scope.prevState, {arg: 'Robert'}); //due to scope inheritance
  };

但是,只要$rootScope 重新初始化,您的代码就会向AnotherCtrl注册越来越多的事件处理程序。您的代码的第二个问题是第一次加载页面时$stateChangeSuccess事件不会触发https://github.com/angular-ui/ui-router/issues/299

您应该只在.run块中注册一次事件处理程序:

angular.module('testapp')
       .run(["$rootScope",function ($rootScope){
                $rootScope.$on('$stateChangeError',
                   function (event, toState, toParams, fromState, fromParams, error) {
                      console.log("Error");
                      if (toState.name === 'anotherState') {
                        $rootScope.prevState = fromState.name;
                      }
                   });

               $rootScope.$on('$stateChangeSuccess',
                  function (event, toState, toParams, fromState, fromParams) {
                    console.log("Success");
                    if (toState.name === 'anotherState') {
                        $rootScope.prevState = fromState.name;
                    }
                });  
            }]);

只有这些代码应保留在您的控制器中:

$scope.goBack = function () {
    $state.transitionTo($rootScope.prevState, {arg: 'Robert'});
    //or $state.transitionTo($scope.prevState, {arg: 'Robert'}); //due to scope inheritance
 };

答案 1 :(得分:0)

您面临的问题是JavaScript闭包的工作原理。当您在函数goBack()的范围内访问prevState以及与stateChangeSuccess相关联的函数时,您将保留父亲的prevState句柄。但JavaScript中的字符串是不可变的。因此,当在goBack中为prevState分配值时,会创建一个新字符串,并且goBack()内的prevState指向该字符串。但是与stateChangeSuccess相关联的prevState仍然指向&#34; null_state&#34;。

您可以做的是将prevState包装在对象中:

var prevData = {prevState: 'null_state'};

并使用prevData.prevState修改prevState。这将起作用的原因是因为所有闭包都保存对同一对象的引用(因为现在您使用的是对象而不是字符串),因此对字符串的更改将传播到不同的函数。

有关详细信息,请仔细阅读以下内容:

How do JavaScript closures work?

Pass a string by reference in Javascript

答案 2 :(得分:0)

每次更改路线时,当前控制器被销毁(广播$ destroy事件)并创建新控制器。

您可以看到控制器在控制器内被销毁的时间:

$scope.$on('$destroy',function () {
   console.log("The controller is destroyed.");
});

在应用程序中维护数据的方法是通过服务(如$ state)或 $ rootScope

这意味着在创建控制器时,您可以从服务获取数据。为此创建一个getter函数。

另一方面,如果要将数据与控制器绑定,则必须更新$ scope.state,而不是prevState。模板仅更新$ scope的值。

结果:

angular.module('testapp')
.controller('AnotherCtrl',
['$scope', '$rootScope', '$state',
    function ($scope, $rootScope, $state) {
        'use strict';
        console.log("Init prevState.");
        $scope.state = $state.getState(); //create the getter


        $rootScope.$on('$stateChangeError',
            function (event, toState, toParams, fromState, fromParams, error) {
                console.log("Error");
                if (toState.name === 'anotherState') {

                    console.log("Old State:" + $scope.state);
                    $scope.state = fromState.name;
                    console.log("New State:" + $scope.state);


                }
            })

        $rootScope.$on('$stateChangeSuccess',
            function (event, toState, toParams, fromState, fromParams) {
                console.log("Success");
                if (toState.name === 'anotherState') {

                    console.log("Old State:" + $scope.state);
                    $scope.state = fromState.name;
                    console.log("New State:" + $scope.state);

                }
            })


        $scope.goBack = function () {
            //$scope.state = 'anotherState';
            console.log("goBack:" + $scope.state);
            $state.transitionTo($scope.state, {arg: 'Robert'});
        };
    }]);

答案 3 :(得分:0)

可能你想改变:

var prevState = 'null_state';

$scope.prevState = 'null_state';

并相应地修复其他内容。