question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. ItĀ collects links to all the places you might be looking at while hunting down a tough bug.

And, if youā€™re still stuck at the end, weā€™re happy to hop on a call to see how we can help out.

A runtime error is thrown when calling `detectChanges` inside the `transform` method of a pipe.

See original GitHub issue

šŸž bug report

Affected Package

The issue is caused by package @angular/core.

Is this a regression?

Description

A runtime error is thrown when calling detectChanges inside the transform method of a pipe.

šŸ”¬ Minimal Reproduction

A reproduction of this issue can be found here.

šŸ”„ Exception or Error

core.js:4200 ERROR TypeError: Cannot read property 'call' of undefined
    at callHook (core.js:3045)
    at callHooks (core.js:3008)
    at executeCheckHooks (core.js:2941)
    at selectIndexInternal (core.js:6177)
    at Module.ɵɵadvance (core.js:6156)
    at AppComponent_Template (app.component.html:1)
    at executeTemplate (core.js:7306)
    at refreshView (core.js:7175)
    at refreshComponent (core.js:8329)
    at refreshChildComponents (core.js:6968)

šŸŒ Your Environment

Angular Version:

Angular CLI: 10.0.7
Node: 12.18.3
OS: darwin x64

Angular: 10.0.12
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router
Ivy Workspace: Yes

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.1000.7
@angular-devkit/build-angular     0.1000.7
@angular-devkit/build-optimizer   0.1000.7
@angular-devkit/build-webpack     0.1000.7
@angular-devkit/core              10.0.7
@angular-devkit/schematics        10.0.7
@angular/cdk                      10.1.3
@angular/cli                      10.0.7
@angular/material                 10.1.3
@ngtools/webpack                  10.0.7
@schematics/angular               10.0.7
@schematics/update                0.1000.7
rxjs                              6.5.5
typescript                        3.9.7
webpack                           4.43.0

Anything else relevant?

This issue only happens when Ivy is enabled.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:1
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
crisbetocommented, Aug 29, 2020

I spent some more time on it and I found a couple of solutions, but they ended up breaking other functionality so I think Iā€™ll leave this for somebody that knows the hook logic better. Hereā€™s what Iā€™ve got so far:

  1. It doesnā€™t break due to the pipe in particular, but because ViewContainerRef.detectChanges is called at the wrong time. The issue can be reproduced by calling it inside an input setter of a directive that has ngAfterViewInit and ngOnChanges hooks. Hereā€™s an even simpler test case:
it('should not throw when calling detectChanges from setter', () => {
  @Directive({selector: '[dir]'})
  class Dir implements OnChanges, AfterViewInit {
    constructor(private _changeDetectorRef: ChangeDetectorRef) {}

    @Input('dir')
    set value(_value: any) {
      this._changeDetectorRef.detectChanges(); // Triggers the error.
    }
    ngOnChanges() {}
    ngAfterViewInit() {}
  }

  @Component({template: `<div [dir]="value">{{value}}</div>`})
  class App {
    value = 1;
  }

  TestBed.configureTestingModule({declarations: [App, Dir]});
  const fixture = TestBed.createComponent(App);
  fixture.detectChanges();
});
  1. I think, the error happens because we re-enter refreshView in the middle of it running the init hooks. In this particular case, it breaks when itā€™s trying to run ngAfterViewInit, but the same problem can be observed if we change it to ngAfterContentInit, ngAfterViewChecked or ngAfterContentChecked.
  2. Weā€™ve dealt with a re-entrant change detection issue like this in the past by moving some of the LView state into the global store (see https://github.com/angular/angular/commit/e16f75db566e7bb22d8b8c1566f8ed5be6874be0). I suspect that we may have to do the same for LView[FLAGS], but I donā€™t know what the side-effects of doing so would be.
  3. The way I tried to fix it was to update the flags which determine whether a hook is allowed to run before actually running the hook. This worked for the test case above, but it broke a bunch of unrelated tests.
2reactions
crisbetocommented, Aug 27, 2020

Iā€™ve been looking into it. It seems to happen if there is an init hook, an ngOnChanges hook and a binding with a pipe on the same element. Hereā€™s a simplified test.

fit('', () => {
  @Pipe({name: 'testPipe', pure: true})
  class TestPipe implements PipeTransform {
    constructor(private _changeDetectorRef: ChangeDetectorRef) {}

    transform(value: any) {
      this._changeDetectorRef.detectChanges();
      return value;
    }
  }

  @Component({
    template: `<div [dir]="1 | testPipe">{{1 | testPipe}}</div>`,
  })
  class App {
  }

  @Directive({selector: '[dir]'})
  class Dir {
    @Input('dir') value: any;
    ngOnChanges() {}
    ngAfterViewInit() {}
  }

  TestBed.configureTestingModule({declarations: [App, Dir, TestPipe]});
  const fixture = TestBed.createComponent(App);
  fixture.detectChanges();
});

Iā€™ll try to either resolve it or post my findings here.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Attempt to use a destroyed view: detectChanges
The issue clearly comes from detectChanges() because the changes were done and the method called during the destroy phase of the component.
Read more >
NG0100: Expression has changed after it was checked
Angular throws an ExpressionChangedAfterItHasBeenCheckedError when an expression value has been changed after change detection has completed. Angular onlyĀ ...
Read more >
fixture.detectchanges() throws error
As described in the Angular testing tutorial, the component isn't initialized fully until you call fixture.detectChanges() the first time.
Read more >
Testing Pipes ā€“ Testing Angular
An Angular Pipe is a special function that is called from a Component ... In essence, a Pipe is class with a public...
Read more >
Guide on Unit testing in Angular and NgRx using Jasmine
Pipe Testing. A pipe is the easiest portion of an application to test because its class usually only contains one method called transform()...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found