Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2: Watch an external variable outside of angular

I want to be able to watch and update when a variable outside of angular2 changes. So let's say I have this in an external javascript file:

var test = 1;

How can I bind this variable to a property in a component?

@Component({
   ...
})

export class MyComponent {
    watchy = window.test;
}

Apparently this should just work, according to this answer.
But it doesn't. If I change the variable in the console, the variable does not update displayed in a template. Am I missing something?

like image 749
Sebastian Olsen Avatar asked Apr 24 '16 21:04

Sebastian Olsen


2 Answers

Angular only runs change detection when an async executed function completes. The function needs to be run inside Angulars zone for angular to recognize the async operation.

Because your variable is changed from outside Angulars zone. Angular doesn't run change detection.

You need to invoke change detection manually for Angular to recognize the changed variable. See also Triggering Angular2 change detection manually

If you for example can dispatch an event instead of just setting a variable, you can listen to the event.

window.dispatchEvent(new CustomEvent('test', {detail: 'newValue'}));
@Component({
   ...
})
export class MyComponent {
    @HostListener('window:test', ['$event'])
    testListener(event) {
      this.watchy = event.detail;
    }
}

Invoked event handlers automatically invoke Angulars change detection, therefore there is nothing more to do.

like image 151
Günter Zöchbauer Avatar answered Oct 20 '22 00:10

Günter Zöchbauer


Apparently this should just work, according to this answer.

I'm not sure how you jumped to that conclusion, but no matter, there is a bigger issue with the code. This line of code

watchy = window.test;

Will create a component property that is a primitive type. When that line of code executes, watchy will be assigned the value 1. watchy, since it is a primitive, has no relationship with window.test after the assignment is made – it simply gets a copy of the window.test value for the assignment. So, if you then change the value of window.test, JavaScript won't update watchy, so Angular change detection isn't even a factor here.

If you want the component property to be linked to the global variable, you could wrap your primitive type inside an object:

var myObj = { test: 1}
export class MyComponent {
  watchy = window.myObj;
}

Now, watchy is a reference type, and it refers to the myObj object – it does not get a copy of the object, it just "points" to it. So, if you then change myObj.test, then watchy will "see" the new value, since it is still pointing to the myObj.test object. But Angular change detection won't notice if you change the value outside the Angular zone.

If you are displaying the value of test in a component template, you will need to change test inside the Angular zone in order for change detection to run and notice the change. Instead of repeating a bunch of code here, see Angular 2 How to get Angular to detect changes made outside Angular?


Günter's answer is another approach: set up an event listener inside Angular (hence inside the Angular zone), then fire that event whenever test changes.

like image 26
Mark Rajcok Avatar answered Oct 20 '22 00:10

Mark Rajcok