fix(ivy): reset binding index before executing a template in `refreshView` call (#32201)
Prior to this change, the `BINDING_INDEX` of a given lView was reset after processing a template. However change detection can be triggered as a result of View queries processing, thus leading to subsequent `refreshView` call (and executing a template), which in turn operates with the binding index that is not reset after the previous `refreshView` call. This commit updates the logic to reset binding index before we execute a template, so binding index is correct for instructions inside template function. PR Close #32201
This commit is contained in:
parent
4f7c971ee7
commit
6b245a39ee
|
@ -377,14 +377,14 @@ export function refreshView<T>(
|
||||||
try {
|
try {
|
||||||
resetPreOrderHookFlags(lView);
|
resetPreOrderHookFlags(lView);
|
||||||
|
|
||||||
if (templateFn !== null) {
|
|
||||||
executeTemplate(lView, templateFn, RenderFlags.Update, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resetting the bindingIndex of the current LView as the next steps may trigger change
|
// Resetting the bindingIndex of the current LView as the next steps may trigger change
|
||||||
// detection.
|
// detection.
|
||||||
lView[BINDING_INDEX] = tView.bindingStartIndex;
|
lView[BINDING_INDEX] = tView.bindingStartIndex;
|
||||||
|
|
||||||
|
if (templateFn !== null) {
|
||||||
|
executeTemplate(lView, templateFn, RenderFlags.Update, context);
|
||||||
|
}
|
||||||
|
|
||||||
const checkNoChangesMode = getCheckNoChangesMode();
|
const checkNoChangesMode = getCheckNoChangesMode();
|
||||||
const hooksInitPhaseCompleted =
|
const hooksInitPhaseCompleted =
|
||||||
(flags & LViewFlags.InitPhaseStateMask) === InitPhaseState.InitPhaseCompleted;
|
(flags & LViewFlags.InitPhaseStateMask) === InitPhaseState.InitPhaseCompleted;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
|
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {ApplicationRef, ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, DoCheck, EmbeddedViewRef, ErrorHandler, Input, NgModule, OnInit, TemplateRef, Type, ViewChild, ViewContainerRef} from '@angular/core';
|
import {ApplicationRef, ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, DoCheck, EmbeddedViewRef, ErrorHandler, Input, NgModule, OnInit, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
|
|
||||||
|
@ -545,6 +545,44 @@ describe('change detection', () => {
|
||||||
expect(fixture.nativeElement.textContent).toEqual('1');
|
expect(fixture.nativeElement.textContent).toEqual('1');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should support change detection triggered as a result of View queries processing', () => {
|
||||||
|
@Component({
|
||||||
|
selector: 'app',
|
||||||
|
template: `
|
||||||
|
<div *ngIf="visible" #ref>Visible text</div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
@ViewChildren('ref')
|
||||||
|
ref !: QueryList<any>;
|
||||||
|
|
||||||
|
visible = false;
|
||||||
|
|
||||||
|
constructor(public changeDetectorRef: ChangeDetectorRef) {}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
this.ref.changes.subscribe((refs: QueryList<any>) => {
|
||||||
|
this.visible = false;
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App],
|
||||||
|
imports: [CommonModule],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement.textContent).toBe('');
|
||||||
|
|
||||||
|
// even though we set "visible" to `true`, we do not expect any content to be displayed,
|
||||||
|
// since the flag is overridden in `ngAfterViewInit` back to `false`
|
||||||
|
fixture.componentInstance.visible = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement.textContent).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
describe('dynamic views', () => {
|
describe('dynamic views', () => {
|
||||||
@Component({selector: 'structural-comp', template: '{{ value }}'})
|
@Component({selector: 'structural-comp', template: '{{ value }}'})
|
||||||
class StructuralComp {
|
class StructuralComp {
|
||||||
|
|
Loading…
Reference in New Issue