Reputation: 755
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
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
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