Rob Rob - 1 month ago 15
Javascript Question

Angular2 Calling exposed method from outside app and loses change binding

I have a public method that I exposed to

window
. This method talks to a
Component
and modifies a variable I am watching in my template. But when I change the value, the
*ngIf()
does not get triggered.

app.component

constructor(private _public: PublicService,) {
window.angular = {methods: this._public};
}


PublicService

export class PublicService {

constructor(
private _viewManager: ViewManagerComponent,
) {}

CallMe(){
this._viewManager.renderView('page1')
}
}


LayoutManagerComponent

@Component({
selector: 'view-manager',
template: `<page *ngIf="view == 'page1'"></page>`
})
export class ViewManagerComponent {
//This is the variable being watched
view = "page";

renderView = function(type){
console.log(type)
this.view = type;
console.log(this.view)
};
}


So the idea is that when the view initially loads, the view is blank. Then when I type
angular.methods.CallMe()
it modifies the
view
variable to
page1
which should then show the html for the Component. If I console
renderView
function it is successfully getting called, just the view does not change.

----Update - Still not working -------

export class ViewManagerComponent {
constructor(private zone:NgZone,private cdRef:ChangeDetectorRef) {

}
view = "page";

@Output() renderView(type){
// type is 'page'
console.log(this.view)
this.zone.run(() => {
// type is 'page'
console.log(this.view)
this.view = type;
// type is 'page1'
console.log(this.view)
});
// type is 'page1'
console.log(this.view)
//cdRef errors:
//view-manager.component.ts:36 Uncaught TypeError: this.cdRef.detectChanges is not a function(…)
this.cdRef.detectChanges();
};

}

Answer

In this case Angular2 doesn't know that it needs to run change detection because the change is caused by code that runs outside Angulars zone.

Run change detection explicitely

contructor(private cdRef:ChangeDetectorRef) {}

someMethodCalledFromOutside() {
  // code that changes properties in this component 
  this.cdRef.detectChanges();
}

Run the code that modifies the components properties inside Angulars zone explicitely

contructor(private zone:NgZone) {}

someMethodCalledFromOutside() {
  this.zone.run(() => {
  // code that changes properties in this component 
  });
}

The zone method is a better fit when // code that changes properties in this component not only changes properties of the current component, but also causes changes to other components (like this.router.navigate(), call method references of methods of other components) because zone.run() executes the code inside Angulars zone, and you don't need to explicitely take care of change detection in every component where a change might happen because of this call.

If you use function(...) instead of () => it's likely you'll get unexpected behavior with this in code inside the Angular component.

See also my answer to this similar question for more details Angular 2 - communication of typescript functions with external js libraries

update

export class ViewManagerComponent {
    constructor(private zone:NgZone,private cdRef:ChangeDetectorRef) {
      self = this;
    }
    view = "page";

     @Output() renderView(type){
        // type is 'page'
        console.log(self.view)
        self.zone.run(() => {
            // type is 'page'
            console.log(self.view)
            self.view = type;
            // type is 'page1'
            console.log(self.view)
        });
        // type is 'page1'
        console.log(self.view)
        self.cdRef.detectChanges();
    };

}
Comments