3

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();
    };

}
Rob
  • 11,185
  • 10
  • 36
  • 54

1 Answers1

3

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();
    };

}
Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Thanks. I made the changes and first tried `this.zone.run` but it didnt work(no errors). I then tried the `cfRef.detectChanges()` and first complained about it not being a provider. I added to `app.module`, then got this error: `view-manager.component.ts:30 Uncaught TypeError: this.cdRef.detectChanges is not a function(…)` – Rob Oct 11 '16 at 15:01
  • It's probably because of `this` not pointing to your current component instance as mentioned in my answer. The answer I linked to should provide a solution for that – Günter Zöchbauer Oct 11 '16 at 15:05
  • I modified my code. Please see above. I placed several consoles before and after to track and `this` seems to be scoped correctly. – Rob Oct 11 '16 at 17:52
  • Why don't you just change `@Output() renderView = function(type){` to `@Output() renderView(type){`? – Günter Zöchbauer Oct 11 '16 at 17:55
  • Please add the error message to your question if you get one. – Günter Zöchbauer Oct 11 '16 at 17:56
  • changed render view format and added error in code comment when I uncomment `this.cdRef.detectChanges();` – Rob Oct 11 '16 at 18:02
  • That worked! Can you tell me why so i can look out for issues like this? – Rob Oct 11 '16 at 18:12
  • If you pass `this.renderView`then `this` inside `renderView()` doesn't point to the `ViewManagerComponent` but to the context where it is actually called from (a nice JS feature ;-) ). You can use `window.xxx = this.renderView.bind(this);` or `window.xxx = (type) => this.renderView(type);` or the way I showed above. Your `console.log()`s seemed to work but I assume that the values were just set from previous calls in the context `this` was pointing to. – Günter Zöchbauer Oct 11 '16 at 18:17