refactor(ivy): introduce a firstUpdatePass
flag for TView
instances (#31270)
This patch introduces a `firstUpdatePass` flag which can be used inside of instruction code to determine if this is the first time each instruction is running inside of the update block of a template or a hostBindings function. PR Close #31270
This commit is contained in:
parent
e3189f97ff
commit
91147ade2e
@ -12,7 +12,7 @@
|
|||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime-es2015": 1485,
|
"runtime-es2015": 1485,
|
||||||
"main-es2015": 14861,
|
"main-es2015": 15039,
|
||||||
"polyfills-es2015": 36808
|
"polyfills-es2015": 36808
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,7 @@ export const TViewConstructor = class TView implements ITView {
|
|||||||
public expandoStartIndex: number, //
|
public expandoStartIndex: number, //
|
||||||
public expandoInstructions: ExpandoInstructions|null, //
|
public expandoInstructions: ExpandoInstructions|null, //
|
||||||
public firstTemplatePass: boolean, //
|
public firstTemplatePass: boolean, //
|
||||||
|
public firstUpdatePass: boolean, //
|
||||||
public staticViewQueries: boolean, //
|
public staticViewQueries: boolean, //
|
||||||
public staticContentQueries: boolean, //
|
public staticContentQueries: boolean, //
|
||||||
public preOrderHooks: HookData|null, //
|
public preOrderHooks: HookData|null, //
|
||||||
|
@ -463,6 +463,9 @@ export function refreshView<T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
|
if (tView.firstUpdatePass === true) {
|
||||||
|
tView.firstUpdatePass = false;
|
||||||
|
}
|
||||||
lView[FLAGS] &= ~(LViewFlags.Dirty | LViewFlags.FirstLViewPass);
|
lView[FLAGS] &= ~(LViewFlags.Dirty | LViewFlags.FirstLViewPass);
|
||||||
leaveViewProcessExit();
|
leaveViewProcessExit();
|
||||||
}
|
}
|
||||||
@ -609,6 +612,7 @@ export function createTView(
|
|||||||
initialViewLength, // expandoStartIndex: number,
|
initialViewLength, // expandoStartIndex: number,
|
||||||
null, // expandoInstructions: ExpandoInstructions|null,
|
null, // expandoInstructions: ExpandoInstructions|null,
|
||||||
true, // firstTemplatePass: boolean,
|
true, // firstTemplatePass: boolean,
|
||||||
|
true, // firstUpdatePass: boolean,
|
||||||
false, // staticViewQueries: boolean,
|
false, // staticViewQueries: boolean,
|
||||||
false, // staticContentQueries: boolean,
|
false, // staticContentQueries: boolean,
|
||||||
null, // preOrderHooks: HookData|null,
|
null, // preOrderHooks: HookData|null,
|
||||||
@ -640,6 +644,7 @@ export function createTView(
|
|||||||
expandoStartIndex: initialViewLength,
|
expandoStartIndex: initialViewLength,
|
||||||
expandoInstructions: null,
|
expandoInstructions: null,
|
||||||
firstTemplatePass: true,
|
firstTemplatePass: true,
|
||||||
|
firstUpdatePass: true,
|
||||||
staticViewQueries: false,
|
staticViewQueries: false,
|
||||||
staticContentQueries: false,
|
staticContentQueries: false,
|
||||||
preOrderHooks: null,
|
preOrderHooks: null,
|
||||||
|
@ -363,6 +363,9 @@ export interface TView {
|
|||||||
/** Whether or not this template has been processed. */
|
/** Whether or not this template has been processed. */
|
||||||
firstTemplatePass: boolean;
|
firstTemplatePass: boolean;
|
||||||
|
|
||||||
|
/** Whether or not the first update for this element has been processed. */
|
||||||
|
firstUpdatePass: boolean;
|
||||||
|
|
||||||
/** Static data equivalent of LView.data[]. Contains TNodes, PipeDefInternal or TI18n. */
|
/** Static data equivalent of LView.data[]. Contains TNodes, PipeDefInternal or TI18n. */
|
||||||
data: TData;
|
data: TData;
|
||||||
|
|
||||||
|
@ -7,6 +7,9 @@
|
|||||||
*/
|
*/
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {Component, ContentChild, Directive, ElementRef, EventEmitter, HostBinding, HostListener, Input, NgModule, OnInit, Output, Pipe, QueryList, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
|
import {Component, ContentChild, Directive, ElementRef, EventEmitter, HostBinding, HostListener, Input, NgModule, OnInit, Output, Pipe, QueryList, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
|
||||||
|
import {TVIEW} from '@angular/core/src/render3/interfaces/view';
|
||||||
|
import {getLView} from '@angular/core/src/render3/state';
|
||||||
|
import {loadLContext} from '@angular/core/src/render3/util/discovery_utils';
|
||||||
import {ngDevModeResetPerfCounters} from '@angular/core/src/util/ng_dev_mode';
|
import {ngDevModeResetPerfCounters} from '@angular/core/src/util/ng_dev_mode';
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
import {By} from '@angular/platform-browser';
|
import {By} from '@angular/platform-browser';
|
||||||
@ -1877,4 +1880,97 @@ describe('acceptance integration tests', () => {
|
|||||||
const fixture = TestBed.createComponent(Cmp);
|
const fixture = TestBed.createComponent(Cmp);
|
||||||
expect(() => fixture.detectChanges()).toThrowError('this error is expected');
|
expect(() => fixture.detectChanges()).toThrowError('this error is expected');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('tView.firstUpdatePass', () => {
|
||||||
|
function isFirstUpdatePass() {
|
||||||
|
const lView = getLView();
|
||||||
|
const tView = lView[TVIEW];
|
||||||
|
return tView.firstUpdatePass;
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertAttrValues(element: Element, value: string) {
|
||||||
|
expect(element.getAttribute('data-comp')).toEqual(value);
|
||||||
|
expect(element.getAttribute('data-dir')).toEqual(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
onlyInIvy('tView instances are ivy-specific')
|
||||||
|
.it('should be marked with `firstUpdatePass` up until the template and host bindings are evaluated',
|
||||||
|
() => {
|
||||||
|
@Directive({
|
||||||
|
selector: '[dir]',
|
||||||
|
})
|
||||||
|
class Dir {
|
||||||
|
@HostBinding('attr.data-dir')
|
||||||
|
get text() {
|
||||||
|
return isFirstUpdatePass() ? 'first-update-pass' : 'post-update-pass';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: '<div [attr.data-comp]="text" dir></div>',
|
||||||
|
})
|
||||||
|
class Cmp {
|
||||||
|
get text() {
|
||||||
|
return isFirstUpdatePass() ? 'first-update-pass' : 'post-update-pass';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [Cmp, Dir],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(Cmp);
|
||||||
|
fixture.detectChanges(false);
|
||||||
|
const element = fixture.nativeElement.querySelector('div') !;
|
||||||
|
|
||||||
|
assertAttrValues(element, 'first-update-pass');
|
||||||
|
|
||||||
|
fixture.detectChanges(false);
|
||||||
|
|
||||||
|
assertAttrValues(element, 'post-update-pass');
|
||||||
|
});
|
||||||
|
|
||||||
|
onlyInIvy('tView instances are ivy-specific')
|
||||||
|
.it('tView.firstUpdatePass should be applied immediately after the first embedded view is processed',
|
||||||
|
() => {
|
||||||
|
@Directive({
|
||||||
|
selector: '[dir]',
|
||||||
|
})
|
||||||
|
class Dir {
|
||||||
|
@HostBinding('attr.data-dir')
|
||||||
|
get text() {
|
||||||
|
return isFirstUpdatePass() ? 'first-update-pass' : 'post-update-pass';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<div *ngFor="let item of items" dir [attr.data-comp]="text">
|
||||||
|
...
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class Cmp {
|
||||||
|
items = [1, 2, 3];
|
||||||
|
get text() {
|
||||||
|
return isFirstUpdatePass() ? 'first-update-pass' : 'post-update-pass';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [Cmp, Dir],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(Cmp);
|
||||||
|
fixture.detectChanges(false);
|
||||||
|
|
||||||
|
const elements = fixture.nativeElement.querySelectorAll('div');
|
||||||
|
assertAttrValues(elements[0], 'first-update-pass');
|
||||||
|
assertAttrValues(elements[1], 'post-update-pass');
|
||||||
|
assertAttrValues(elements[2], 'post-update-pass');
|
||||||
|
|
||||||
|
fixture.detectChanges(false);
|
||||||
|
assertAttrValues(elements[0], 'post-update-pass');
|
||||||
|
assertAttrValues(elements[1], 'post-update-pass');
|
||||||
|
assertAttrValues(elements[2], 'post-update-pass');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user