问题 Angular:是否可以观察全局变量(即在范围之外)?


首先让我说,我想要做的事情可能不是好的做法。但是,我需要做这样的事情,以便以小的增量步骤将大型Web应用程序迁移到AngularJs。

我试过了

 $scope.$watch(function () { return myVar; }, function (n, old) {
                    alert(n + ' ' + old);
                });

其中myVar是一个全局变量(在窗口上定义)

然后从控制台更改myVar。 但它只在首次设置观察者时触发。

如果我从控制器内更新myVar,它可以工作(参见 http://jsfiddle.net/rasmusvhansen/vsDXz/3/,但不是如果它是从一些传统的JavaScript更新

有没有办法实现这个目标?

更新 如果遗留代码完全没有限制,我喜欢Anders的回答。但是,目前我正在研究这种似乎有用的方法,并且不包括每秒触发的计时器:

// In legacy code when changing stuff    
$('.angular-component').each(function () {
        $(this).scope().$broadcast('changed');
});

// In angular
$scope.$on('changed', function () {
    $scope.reactToChange();
});

我正在向Anders授予积分,尽管我会选择其他解决方案,因为他的解决方案正确地解决了所述的问题。


11466
2018-03-08 12:59


起源

可能不想用 each 对于广播,角度只做一个消化 - charlietfl
在您的示例代码中,做了什么 .angular-component 指向? - mlhDev
就我而言,我选择了我真正想要改变的元素(使用 angular.element(document.getElementById()) 因为我没有使用jQuery),然后调用 scope().$broadcast('customEventName');。请注意,我必须使用 $apply() 在 - 的里面 $on('customEventName') 功能,让观察者被召唤。 - Guy Schalnat


答案:


这里的问题可能是你正在修改 myVar 来自Angular世界之外。 Angular不会一直运行摘要周期/脏检查,只有在应该触发摘要的应用程序中发生事件时,例如Angular知道的DOM事件。即便如此 myVar 已经改变了,Angular认为没有理由开始一个新的摘要周期,因为什么都没发生(至少Angular知道)。

因此,为了激活您的手表,您需要强制Angular在您更改时运行摘要 myVar。但这会有点麻烦,我认为你最好创建一个全局可观察对象,如下所示:

<!doctype html>
<html ng-app="myApp">
<head>
    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="http://code.angularjs.org/1.0.5/angular.min.js"></script>
    <script>
    // Outside of Angular
    window.myVar = {prop: "value1"};
    var myVarWatch = (function() {
        var watches = {};

        return {
            watch: function(callback) {
                var id = Math.random().toString();
                watches[id] = callback;

                // Return a function that removes the listener
                return function() {
                    watches[id] = null;
                    delete watches[id];
                }
            },
            trigger: function() {
                for (var k in watches) {
                    watches[k](window.myVar);
                }
            }
        }
    })();

    setTimeout(function() {
        window.myVar.prop = "new value";
        myVarWatch.trigger();
    }, 1000);

    // Inside of Angular
    angular.module('myApp', []).controller('Ctrl', function($scope) {
        var unbind = myVarWatch.watch(function(newVal) {
            console.log("the value changed!", newVal);
        });

        // Unbind the listener when the scope is destroyed
        $scope.$on('$destroy', unbind);
    });
    </script>
</head>
<body ng-controller="Ctrl">

</body>
</html>

12
2018-03-08 13:25



感谢输入Anders,我认为如果无法更改遗留代码,这是一种非常有用的方法。我可以对遗留代码进行小的更改,所以我想我会采用不同的方法(请参阅我的更新) - rasmusvhansen
顺便问一下,退房吧 Object.observe()。它允许您监视对象的更改。它仍然没有在很多浏览器中实现,但它有polyfill。 - Anders Ekdahl
我需要在angular myVarWatch.watch中使用$ scope。$ apply来对角度进行消化。 - willJk