2

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?

10
  • Why do you want to do that? Commented Nov 13, 2015 at 21:47
  • I'm trying to watch variables in one application and use them in another. Commented Nov 13, 2015 at 21:47
  • From raw JavaScript to AngularJS Commented Nov 13, 2015 at 21:48
  • Is your other application an angular app? Commented Nov 13, 2015 at 21:48
  • It's not an AngularJS app - it's raw JavaScript Commented Nov 13, 2015 at 21:49

2 Answers 2

2

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
});
Sign up to request clarification or add additional context in comments.

2 Comments

why not just call the controller from the other code instead of an EE middleman?
This is an external data, and I usually wrap external sources in a service, which is a singleton and won't be destroyed during the app lifetime.. You can do it in a controller if it suits your taste.
1

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.

1 Comment

There is a problem with this method if you use $compileProvider.debugInfoEnabled(false); - the scope won't be available from outside angular using angular.element(document.getElementById('angular-hook')).scope().

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.