Angular2: Watch an external variable outside of angular
Asked Answered
I

3

7

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?

Ichthyo answered 24/4, 2016 at 21:1 Comment(0)
A
19

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.

Ascarid answered 25/4, 2016 at 4:37 Comment(11)
app/app.component.ts(96,12): error TS2339: Property 'watchy' does not exist on t ype 'AppComponent'.Ensemble
I guess this is a linter warning. Just declare the property on the class and you should be good.Deoxyribonuclease
i am new and from last two days i am getting these errors. when i tried to deciare i got some other errors. piease what is right way to deciareEnsemble
adding watchy:any; to the class should do. If you get an error please create a new question because this is then more an IDE setup issue and not related to the question.Deoxyribonuclease
After checking many times I succeeded somewhere but now got this error.. app/app.component.ts(99,28): error TS2339: Property 'detail' does not exist on type 'Window'.Ensemble
Sorry, that is a mistake in my answer. It should be event.detail and thanks for the hint.Deoxyribonuclease
event doesn't have any property named detail i checked this by logging event variable in console!Ensemble
If you dispatch it as shown above, it should have one. See also developer.mozilla.org/en-US/docs/Web/Guide/Events/…Deoxyribonuclease
I exactly copied your code but detail object is not their.. it is undefined .... I am trying this from today morning and here its evening?????Ensemble
This code, as written, will only work for button/click/etc events. If you simply are trying to send a message (data) through an event, you now use window.dispatchEvent(new CustomEvent('test', {detail: 'newValue'})); Notice, it uses CustomEvent, not Event. The property detail is only available via CustomEvent. You can see this API and more details : developer.mozilla.org/en-US/docs/Web/API/CustomEvent/…Dipteral
Thanks a lot for the hint. Fixed.Deoxyribonuclease
M
3

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.

Mcnamee answered 26/4, 2016 at 18:40 Comment(0)
H
0

This will never work. You need to tell Angular2 to update the watchy, there are many ways to achive this, but it's a bit weird to have it in a global var outside of Angular2 app.

For example you can have the watchy in a function that will be triggered on a click event from an element within the component, something like :

@Component({
   template: '<div (click)="onClickEvent()"></div>{{watchy}}'
})

export class MyComponent {
    watchy = window.test;

   onClickEvent() {
    this.watchy = window.test;
  }
}

Then change the var, trigger the click event and it will work.

Headrail answered 24/4, 2016 at 22:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.