ChaseLouisPowers
ChaseLouisPowers

Reputation: 755

How to Watch Window Variables from Within AngularJS

I have a group of window variables that I want to bind to a scope variable in AngularJS.

Here's my code so far but it doesn't work:

        .directive('watchGlobals', function() {
            return {
                link: function(scope, element, attrs) {
                        var watchGlobals = attrs.watchGlobals.split(',');
                        for (var i = 0; i < watchGlobals.length; i++) {
                            scope[watchGlobals[i]] = window[watchGlobals[i]];
                        }
                }
            }
        });

What's the optimal way to do this? I'm trying to avoid having to use setInterval. I'd like to have the scopes updated whenever the window variable is updated. Is there a way I can watch window variables from within AngularJS like this?

Upvotes: 2

Views: 2160

Answers (2)

Rhumborl
Rhumborl

Reputation: 16609

As the comments says there are many ways this can be done, and pushing data is probably better than listening for it.

You can use a simple hook into angular to simply call a $digest on your existing directive's scope. Then instead of watching the variable value as you are currently, use a function to return the current value of the window property. This way you don't lose the watch if the value is a simple type (string, number) or is replaced:

/*** angular code ***/

angular.module('myApp', [])

    .controller('ctrl', function() {})

    // inject the standard angular $window service (really just the window object)
    .directive('watchGlobals', function($window) {
        return {
            link: function(scope, element, attrs) {

                // add a method to the raw DOM element that JS can call to update the scope
                element[0].externalUpdate = function() {
                    scope.$digest();
                };

                var watchGlobals = attrs.watchGlobals.split(',');
                // loop as before, but use an IIFE to isolate the variable name
                for (var i = 0; i < watchGlobals.length; i++) {
                    (function(variable) {
                        scope.$watch(function() { return $window[variable]; }, // use a function
                                     function(newVal) { scope[variable] = newVal; }); // update scope
                    })(watchGlobals[i]);
                }
            }
        }
    });

/*** JS code ***/

// global function to update angular scope
function updateAngular() {
    // find the directive element and call it's externalUpdate() function
    // this is the only "bit in the middle" of the 2 apps
    document.getElementById('angular-hook').externalUpdate();

    // alternative option, but breaks with $compileProvider.debugInfoEnabled(false)
    //angular.element(document.getElementById('angular-hook')).scope().$digest();
}

function setValueX() {
    window.xVal = document.getElementById('valX').value;
    updateAngular();
}

function setValueY() {
    window.yVal = document.getElementById('valY').value;
    updateAngular();
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<h2>Angular</h2>
<div ng-app="myApp">
    <div ng-controller="ctrl">
        <div id="angular-hook" watch-globals="xVal,yVal"></div>
        <p>x: {{xVal}}</p>
        <p>y: {{yVal}}</p>
    </div>
</div>

<h2>JS</h2>
<p>X: <input type="text" id="valX" /> <button onclick="setValueX()">Set</button></p>
<p>Y: <input type="text" id="valY" /> <button onclick="setValueY()">Set</button></p>

UPDATE: Changed the scope().$digest() call to a DOM element function to get around turning off debugInfo.

Upvotes: 1

Ori Drori
Ori Drori

Reputation: 191946

Use a simple js pubsub (for example PubSubJs), that you can subscribe to inside a service in angular. The other app should publish via the pubsub, which will invoke the call in the service. The service will update the angular app.

angular.factory('connectService', ['$rootScope', function($rootScope) {

    var token = PubSub.subscribe( 'TOPIC_NAME', function(msg, data) {
        $rootScope.$apply(function() {
            $rootScope.$broadcast('TOPIC_NAME', { msg: msg, data: data });
        });
    });

}]);

From the other app you can now publish the data:

PubSub.publish( 'MY TOPIC', 'hello world!' );

Now whenever you want to want to get the data in angular use:

$scope.$on('TOPIC_NAME', function(data) {
    console.log(data.msg, data.data); // do whatever you want to do with the data
});

Upvotes: 2

Related Questions