Javascript 变量更改在my$scope函数中不可见

Javascript 变量更改在my$scope函数中不可见,javascript,angularjs,Javascript,Angularjs,当发生状态更改事件时,即使在事件侦听器中看到变量已更新,但变量未更新为$scope函数,我对此有问题 代码如下: angular.module('testapp') .controller('AnotherCtrl', ['$scope', '$rootScope', '$state', function ($scope, $rootScope, $state) { 'use strict'; console.log("Init prevState.");

当发生状态更改事件时,即使在事件侦听器中看到变量已更新,但变量未更新为
$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>

另一种观点


状态:{{State}}

返回
以下是控制台输出:

因此,当我按下按钮上调用函数
goBack()
的按钮时,
prevState
变量仍然是
'null\u state'

有人能解释问题出在哪里吗

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

根本原因是每当更改状态时,angular都会重新初始化与当前状态关联的控制器,从而清除先前存储的状态并重新初始化

因此,解决方案是将以前的状态存储在共享服务中。在这种情况下,我们可以只使用
$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
  };
但是,每当
另一个Ctrl
重新初始化时,您的代码就会遇到一个严重的问题:将越来越多的事件处理程序注册到
$rootScope
。代码的第二个问题是首次加载页面时不会触发
$stateChangeSuccess
事件

您应该只在
中注册一次事件处理程序。运行
块:

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
 };
$scope.$on('$destroy',function () {
   console.log("The controller is destroyed.");
});

您面临的问题是JavaScript闭包的工作方式。当您在函数goBack()和与stateChangeSuccess关联的函数的作用域中访问prevState时,您将保留父级prevState的句柄。但是JavaScript中的字符串是不可变的。因此,在goBack中为prevState赋值时,将创建一个新字符串,goBack()中的prevState将指向该字符串。但是与stateChangeSuccess关联的prevState仍然指向“null_状态”

您可以将prevState包装到对象中,如下所示:

var prevData = {prevState: 'null_state'};
并使用
prevData.prevState
修改prevState。这将起作用的原因是,所有闭包都持有对同一对象的引用(因为现在使用的是对象而不是字符串),因此对字符串的更改将传播到不同的函数

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


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

当控制器被破坏时,您可以在控制器内看到:

$scope.goBack = function () {
    $state.transitionTo($rootScope.prevState, {arg: 'Robert'});
    //or $state.transitionTo($scope.prevState, {arg: 'Robert'}); //due to scope inheritance
 };
$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'});
        };
    }]);

您可能想要更改:

var prevState = 'null_state';


并相应地修复其他内容。

您也可以共享与此控制器相关的HTML吗?我也这么认为,但如果控制器重新初始化,为什么“Init prevState”控制台打印不会显示在他的控制台屏幕截图上?这是控制器做的第一件事。@haimlit:控制台中有
“Init prevState.”
。请仔细看看。但是如果你说的是正确的,那么应该有两种情况——一种是控制器初始化时,另一种是由于状态更改而重新初始化时。@haimlit:看看代码,我们可以看出他来自
“modeltest.transfer”
,应该只有一个
“Init prevState”
。我使用的是angular 1.2.17,流程如下:
$stateChangeSuccess
在重新初始化控制器之前首先触发(在控制台中记录
“Init prevState.”
)。我还觉得控制台有问题,因为
“Init prevState”。
应该介于
“New State”
“go back”
之间。不确定在其他版本中,控制器初始化是在
$stateChangeSuccess
@haimlit之前运行的:我不确定他是如何重现这种情况的。这可能与我上面提到的问题有关。代码第一次不起作用,每当我们达到这种状态时,事件处理程序就会被注册得越来越多。