diff --git a/packages/core/src/application/application_ref.ts b/packages/core/src/application/application_ref.ts index 66916e73f970d..a83279819dd3a 100644 --- a/packages/core/src/application/application_ref.ts +++ b/packages/core/src/application/application_ref.ts @@ -767,5 +767,8 @@ export function whenStable(applicationRef: ApplicationRef): Promise { function shouldRecheckView(view: LView): boolean { - return requiresRefreshOrTraversal(view) || !!(view[FLAGS] & LViewFlags.Dirty); + return requiresRefreshOrTraversal(view); + // TODO(atscott): We need to support rechecking views marked dirty again in afterRender hooks + // in order to support the transition to zoneless. b/308152025 + /* || !!(view[FLAGS] & LViewFlags.Dirty); */ } diff --git a/packages/core/test/acceptance/after_render_hook_spec.ts b/packages/core/test/acceptance/after_render_hook_spec.ts index 584f4af825b8b..8568b28db1aff 100644 --- a/packages/core/test/acceptance/after_render_hook_spec.ts +++ b/packages/core/test/acceptance/after_render_hook_spec.ts @@ -946,7 +946,8 @@ describe('after render hooks', () => { const appRef = TestBed.inject(ApplicationRef); appRef.attachView(fixture.componentRef.hostView); appRef.tick(); - expect(fixture.nativeElement.innerText).toBe('1'); + // TODO(atscott): We need to support this for zoneless to succeed + expect(fixture.nativeElement.innerText).toBe('0'); }); it('throws error when causing infinite updates', () => { diff --git a/packages/core/test/acceptance/change_detection_transplanted_view_spec.ts b/packages/core/test/acceptance/change_detection_transplanted_view_spec.ts index d9639e97d08e4..7a5be5fe4f416 100644 --- a/packages/core/test/acceptance/change_detection_transplanted_view_spec.ts +++ b/packages/core/test/acceptance/change_detection_transplanted_view_spec.ts @@ -6,9 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {CommonModule} from '@angular/common'; -import {ChangeDetectionStrategy, ChangeDetectorRef, Component, computed, Directive, DoCheck, inject, Input, signal, TemplateRef, Type, ViewChild, ViewContainerRef} from '@angular/core'; -import {AfterViewChecked, EmbeddedViewRef} from '@angular/core/src/core'; +import {CommonModule, NgTemplateOutlet} from '@angular/common'; +import {AfterViewChecked, ApplicationRef, ChangeDetectionStrategy, ChangeDetectorRef, Component, computed, createComponent, Directive, DoCheck, EmbeddedViewRef, EnvironmentInjector, ErrorHandler, inject, Input, signal, TemplateRef, Type, ViewChild, ViewContainerRef} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -1013,6 +1012,63 @@ describe('change detection for transplanted views', () => { expect(fixture.nativeElement.innerText).toEqual('new'); }); }); + + it('can call markForCheck inside insertion tree when inserted as backwards reference', () => { + @Component({selector: 'markForCheck', template: '', standalone: true}) + class MarkForCheck { + cdr = inject(ChangeDetectorRef); + ngDoCheck() { + this.cdr.markForCheck(); + } + } + + @Component({ + selector: 'insertion', + imports: [NgTemplateOutlet], + standalone: true, + template: ` `, + }) + class Insertion { + @Input() template!: TemplateRef<{}>; + constructor(readonly changeDetectorRef: ChangeDetectorRef) {} + } + + @Component({ + imports: [MarkForCheck, Insertion], + template: ` `, + standalone: true, + selector: 'declaration' + }) + class Declaration { + @ViewChild('myTmpl', {static: true}) template!: TemplateRef<{}>; + } + @Component({ + standalone: true, + imports: [Declaration, Insertion], + template: '' + }) + class App { + } + + TestBed.configureTestingModule({ + providers: [{ + provide: ErrorHandler, useClass: class extends ErrorHandler { + override handleError(e: any) { + throw e; + } + } + }] + }); + + const app = createComponent(App, {environmentInjector: TestBed.inject(EnvironmentInjector)}); + const appRef = TestBed.inject(ApplicationRef); + appRef.attachView(app.hostView); + // ApplicationRef has a loop to continue refreshing dirty views. If done incorrectly, refreshing + // the backwards reference transplanted view can cause an infinite loop because it goes and + // marks the root view dirty, which then starts the process all over again by checking the + // declaration. + expect(() => appRef.tick()).not.toThrow(); + }); }); function trim(text: string|null): string {