Search code examples
angularangular-directiveangular2-hostbinding

ExpressionChangedAfterItHasBeenCheckedError when passing the width of an element to my own directive.


I created my own directive for table header (thead tag). That makes that header of table is "sticky" (has fixed position and is visible during scroll table). It looks like that:

sticky.directive.ts

const NAVBAR_HEIGHT = 55;

@Directive({
    selector: '[sticky]',
})
export class StickyDirective implements AfterViewInit {

    @Input()
    @HostBinding('style.width.px')
    stickyWidth: string;

    topPosition: number;

    constructor(private _element: ElementRef, private _window: WindowRef) { }

    ngAfterViewInit() {
        let boundingClientRect = this._element.nativeElement.getBoundingClientRect();
        this.topPosition = boundingClientRect.top - NAVBAR_HEIGHT;
    }

    @HostListener('window:scroll', ['$event'])
    handleScrollEvent(e) {
        if (this._window.nativeWindow.pageYOffset > (this.topPosition) ) {
            this._element.nativeElement.classList.add('stick');
        } else {
            this._element.nativeElement.classList.remove('stick');
        }
    }
} 

And is called in this way:

<div class="card-block" >
   <table #table class="table table-bordered">
      <thead sticky [stickyWidth]="table.offsetWidth">

It's work fine, but in console I got error:

ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: '1632'. Current value: '1615'.
    at viewDebugError (core.es5.js:8633)
    at expressionChangedAfterItHasBeenCheckedError (core.es5.js:8611)
    at checkBindingNoChanges (core.es5.js:8775)
    at checkNoChangesNodeInline (core.es5.js:12329)
    at checkNoChangesNode (core.es5.js:12303)
    at debugCheckNoChangesNode (core.es5.js:12922)
    at debugCheckDirectivesFn (core.es5.js:12824)
    at Object.View_PageListComponent_0.co [as updateDirectives] (PageListComponent.html:23)
    at Object.debugUpdateDirectives [as updateDirectives] (core.es5.js:12806)
    at checkNoChangesView (core.es5.js:12123)
    at callViewAction (core.es5.js:12487)

Anyone can explain me what I did wrong? How can I secure my app against this exception or what is the correct way creating similar directive? Thanks in advance


Solution

  • the moment you're updating table.offsetWidth is happening after Angular has finished it's changeDetection cycle .

    It would be better to share the code, but without seeing it, here is what you possibly need to do :

    • Move that update in better and proper timing, and make sure the process of changing that is a one way flow.

    • Use detectorRef.detectChanges() after your update

    • Put the update inside a setTimeout

    Any of them would work, but best is the first one, the others are your last resort