简体   繁体   中英

Angular2 observable change detection template update

For performance reasons, I am trying to set up manual change detection on my components.

The structure of the app: App -> Book(s) -> Page(s)

I subscribe to an observable in the AppComponent, I then run the "markForCheck" method of the ChangeDetectorRef.

This seems to trigger the ngAfterContentChecked method in the BookComponent (the child of the component I marked for update).

This made me believe that "markForCheck" in a parent component also updates all the children.

However, even though ngAfterContentChecked is executed in the child (BookComponent), the template of this component doesn't update!

Am I supposed to listen to the observable in every child (sometimes several levels deep in the component hierarchy)? Or did I do something wrong? And why does ngAfterContentChecked execute in the child components even though the template doesn't update?

Some of the relevant code.

@Component({
    selector: 'app',
    directives: [BookComponent],
    changeDetection: ChangeDetectionStrategy.OnPush,
    template: `
        <book *ngIf="_book" [book]="_book" [style.height]="_availableDimensions.height+'px'" (window:resize)="onResize($event)"></book>
        <footer id="footer"></footer>
    `
})

export class PlayerComponent {
    ngOnInit() {
        this.initScale();
    }

initScale(){
        this._playerService.availableDimensions$.subscribe(
            availableDimensions => {
                // set local availableDimensions variable
                this._availableDimensions = availableDimensions;
                this._cd.markForCheck();
            }
        );
    }
}



@Component({
    selector: 'book',
    directives: [PageComponent],
    changeDetection: ChangeDetectionStrategy.OnPush,
    template: `
        <div id="pageScrollZone">
            <div id="pageHolder" [ngStyle]="pageHolderStyles()"></div>
        </div>
    `
})

export class BookComponent {
    constructor(
            private _cd: ChangeDetectorRef,
            private _playerService: PlayerService,
            private _config: Config
    ){}

    ngAfterContentChecked(){
// This part does execute when the observable changes
        let date = new Date();
        console.log('BOOK: '+date.getHours()+':'+date.getMinutes()+':'+date.getSeconds());
    }

    pageHolderStyles(){
// This part does NOT execute when the observable changes
        let styles = {
            marginTop: this._currentMargin.y+'px',
            transform: 'scale('+ (this._baseZoom * this._currentZoomLevel) +')'
        }

        return styles;
    }
}

Sooo... Two quick things..

First, when you use markForCheck() it goes between the component ITSELF and the ROOT COMPONENT to mark a path of what Angular should run change detection on.

From a great Thoughtram Article: 树

"We can access a component's ChangeDetectorRef via dependency injection, which comes with an API called markForCheck(). This method does exactly what we need! It marks the path from our component until root to be checked for the next change detection run."

So when you trigger it doesn't look at it's own children, it looks towards the root component. If it's children are also onPush they will ignore it's update.

Am I supposed to listen to the observable in every child (sometimes several levels deep in the component hierarchy)?

Only if you want all the elements to be explicitly onPush. My guess is you want the Pages to update automatically if it's parent does... Well you are in luck! If you use the standard strategy on the pages component and use the onPush strategy on the Book when detection runs down the tree it will SKIP that branch, even if the children are normal components. Source:Victor Savkin

So NO you don't have to listen to the observer deeply, just wherever you want the break to be. Every component being onPush doesn't guarantee a performance gain if you have to trick it into working. In most cases just blocking the critical nodes is enough.

The second thing is about ngAfterContentChecked It's a known bug:

(Exactly same) https://github.com/angular/angular/issues/7055

(Related, better discussion on both) https://github.com/angular/angular/issues/7054

So they know about it, it's not actually running detection, but it's firing the callback...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM