Skip to content
Permalink
Browse files

fix(ivy): reset binding index before executing a template in `refresh…

…View` 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
  • Loading branch information...
AndrewKushnir authored and atscott committed Aug 20, 2019
1 parent 4f7c971 commit 6b245a39ee45dea39b5ad5bd30b2b24527d45ebe
@@ -377,14 +377,14 @@ export function refreshView<T>(
try {
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
// detection.
lView[BINDING_INDEX] = tView.bindingStartIndex;

if (templateFn !== null) {
executeTemplate(lView, templateFn, RenderFlags.Update, context);
}

const checkNoChangesMode = getCheckNoChangesMode();
const hooksInitPhaseCompleted =
(flags & LViewFlags.InitPhaseStateMask) === InitPhaseState.InitPhaseCompleted;
@@ -8,7 +8,7 @@


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 {expect} from '@angular/platform-browser/testing/src/matchers';

@@ -545,6 +545,44 @@ describe('change detection', () => {
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', () => {
@Component({selector: 'structural-comp', template: '{{ value }}'})
class StructuralComp {

0 comments on commit 6b245a3

Please sign in to comment.
You can’t perform that action at this time.