Joe
Joe

Reputation: 3847

Reacting to changes made outside of Angular2

I've got a non-angular page made with fairly basic JS, and thought it'd be a splendid idea to try and add learn some Angular2 and use it for some new functionality.

My plan was that I'd bind an Angular2 component to an object that is being updated by the old code, and I'd use Angular2 magic to update a chunk of UI.

The problem is I cant convince Angular2 to react to any changes made in the outside JS. What the trick to doing that? Attempts at googling the problem lead to in depth explanations of Angular2's change detection process, which hasn't been helpful so far. Is this just an awful idea?

I found a random Angular2 jsfiddle and hacked it up to show the problem. Strings are added to 'window.names', but you dont see them until one is added from the angular side: https://jsfiddle.net/byfo3jg3/ . The code follows:

var names = ['Joe'];

setTimeout(function() {
  names.push("Frank");
}, 1000);

setTimeout(function() {
  names.push("Sterve");
}, 2000);

setTimeout(function() {
  names.push("Garfield");
}, 3000);

(function() {
  var HelloApp,
        ListThing;

    ListThing = ng
        .Component({
            selector: 'list-thing',
            template: '<ul><li *ng-for="#name of names">{{name}}</li></ul>',
            directives: [ng.NgFor]
        })
        .Class({
            constructor: function() {
                this.names = window.names;

        setTimeout(function() {
                    this.names.push("Oh hai");
                }.bind(this), 10000);
            }
        });

    HelloApp = ng
        .Component({
            selector: 'hello-app',
            template: '<list-thing></list-thing>',
            directives: [ListThing]
        })
        .Class({
            constructor: function() {}
        });

    document.addEventListener('DOMContentLoaded', function() {
        ng.bootstrap(HelloApp);
    });
}());

Upvotes: 2

Views: 1290

Answers (3)

Mark Rajcok
Mark Rajcok

Reputation: 364697

Is this just an awful idea?

Yes.

Angular's automatic change detection system assumes that changes to data (that you want your components to display) are happening inside an event handler that is monkey-patched by Zone.js. Because then Angular's change detection will execute when such an event handler fires (well, technically, it will execute after the event handler finishes).

If you want a component view to automatically update, you have to change the bound data inside Angular – inside the Angular zone. As @Jigar answered, you can modify your code to call angularZone.run(_ => // make changes here), but if you have to do that, you might as well move the code that manages and manipulates the data into a service (or a component, if the logic is minimal).

See also Günter's alternative approach: set up an event listener inside Angular (hence inside the Angular zone). Then fire that event whenever you make changes outside the Angular zone.

Upvotes: 2

kemsky
kemsky

Reputation: 15271

names should be component property to work inside of template:

constructor(){this.names = window.names}

Changes to window.names will not be detected by angular, so you have few options: poll names using setInterval(()=>{this.names = window.names}, 1000) or expose global callback:

constructor(zone:NgZone) 
{
   window.notify = ()=> {
        zone.run(()=> { 
                this.names = window.names;
        });
   }
 }

and call it from plain js window.notify() or use other methods to invoke change detection.

Upvotes: 2

Jigar
Jigar

Reputation: 544

You will need to set the NgZone to window object and then call run function of the zone.

Please refer to Angular 2 How to get Angular to detect changes made outside Angular? SO Question

Upvotes: 2

Related Questions