diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index c1d4ebdc40..bbe70a08bc 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -14,7 +14,7 @@ import {Sanitizer} from '../sanitization/security'; import {assertComponentType, assertDefined} from './assert'; import {queueInitHooks, queueLifecycleHooks} from './hooks'; -import {CLEAN_PROMISE, ROOT_DIRECTIVE_INDICES, _getComponentHostLElementNode, baseDirectiveCreate, createLViewData, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, getRootView, hostElement, initChangeDetectorIfExisting, leaveView, locateHostElement, setHostBindings,} from './instructions'; +import {CLEAN_PROMISE, _getComponentHostLElementNode, baseDirectiveCreate, createLViewData, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, getRootView, hostElement, initChangeDetectorIfExisting, leaveView, locateHostElement, setHostBindings, queueHostBindingForCheck,} from './instructions'; import {ComponentDef, ComponentDefInternal, ComponentType} from './interfaces/definition'; import {LElementNode} from './interfaces/node'; import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; @@ -122,6 +122,9 @@ export function renderComponent( // Create directive instance with factory() and store at index 0 in directives array component = baseDirectiveCreate(0, componentDef.factory() as T, componentDef); + if (componentDef.hostBindings) { + queueHostBindingForCheck(0, componentDef.hostVars); + } rootContext.components.push(component); (elementNode.data as LViewData)[CONTEXT] = component; initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data !); @@ -129,7 +132,7 @@ export function renderComponent( opts.hostFeatures && opts.hostFeatures.forEach((feature) => feature(component, componentDef)); executeInitAndContentHooks(); - setHostBindings(ROOT_DIRECTIVE_INDICES); + setHostBindings(rootView[TVIEW].hostBindings); detectChangesInternal(elementNode.data as LViewData, elementNode, component); } finally { leaveView(oldView); diff --git a/packages/core/src/render3/component_ref.ts b/packages/core/src/render3/component_ref.ts index 753bd8e7d8..72cc43db36 100644 --- a/packages/core/src/render3/component_ref.ts +++ b/packages/core/src/render3/component_ref.ts @@ -18,7 +18,7 @@ import {Type} from '../type'; import {assertComponentType, assertDefined} from './assert'; import {LifecycleHooksFeature, createRootContext} from './component'; -import {baseDirectiveCreate, createLNode, createLViewData, createTView, elementCreate, enterView, hostElement, initChangeDetectorIfExisting, locateHostElement, renderEmbeddedTemplate} from './instructions'; +import {adjustBlueprintForNewNode, baseDirectiveCreate, createLNode, createLViewData, createTView, elementCreate, enterView, hostElement, initChangeDetectorIfExisting, locateHostElement, renderEmbeddedTemplate} from './instructions'; import {ComponentDefInternal, ComponentType, RenderFlags} from './interfaces/definition'; import {LElementNode, TNode, TNodeType} from './interfaces/node'; import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; @@ -156,6 +156,7 @@ export class ComponentFactory extends viewEngine_ComponentFactory { let firstTNode: TNode|null = null; let previousTNode: TNode|null = null; for (let j = 0; j < nodeList.length; j++) { + adjustBlueprintForNewNode(rootView); const lNode = createLNode(++index, TNodeType.Element, nodeList[j] as RElement, null, null); if (previousTNode) { diff --git a/packages/core/src/render3/i18n.ts b/packages/core/src/render3/i18n.ts index d5ce437c39..40c387ea08 100644 --- a/packages/core/src/render3/i18n.ts +++ b/packages/core/src/render3/i18n.ts @@ -7,7 +7,7 @@ */ import {assertEqual, assertLessThan} from './assert'; -import {NO_CHANGE, _getViewData, bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4, createLNode, getPreviousOrParentNode, getRenderer, load, resetApplicationState} from './instructions'; +import {NO_CHANGE, _getViewData, adjustBlueprintForNewNode, bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4, createLNode, getPreviousOrParentNode, getRenderer, load, resetApplicationState} from './instructions'; import {RENDER_PARENT} from './interfaces/container'; import {LContainerNode, LNode, TContainerNode, TElementNode, TNodeType} from './interfaces/node'; import {BINDING_INDEX, HEADER_OFFSET, TVIEW} from './interfaces/view'; @@ -330,8 +330,10 @@ export function i18nApply(startIndex: number, instructions: I18nInstruction[]): // If we were to only create a `RNode` then projections won't move the text. // Create text node at the current end of viewData. Must subtract header offset because // createLNode takes a raw index (not adjusted by header offset). + adjustBlueprintForNewNode(viewData); + const lastNodeIndex = viewData.length - 1; const textLNode = - createLNode(viewData.length - HEADER_OFFSET, TNodeType.Element, textRNode, null, null); + createLNode(lastNodeIndex - HEADER_OFFSET, TNodeType.Element, textRNode, null, null); localPreviousNode = appendI18nNode(textLNode, localParentNode, localPreviousNode); resetApplicationState(); break; diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index cf4be6bc31..d2592fd725 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -22,7 +22,7 @@ import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LEleme import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection'; import {LQueries} from './interfaces/query'; import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer'; -import {BINDING_INDEX, CLEANUP, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, SANITIZER, TAIL, TData, TVIEW, TView} from './interfaces/view'; +import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, SANITIZER, TAIL, TData, TVIEW, TView} from './interfaces/view'; import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert'; import {appendChild, appendProjectedNode, canInsertNativeNode, createTextNode, findComponentHost, getChildLNode, getLViewChild, getNextLNode, getParentLNode, insertView, removeView} from './node_manipulation'; import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher'; @@ -48,17 +48,6 @@ const _CLEAN_PROMISE = Promise.resolve(null); */ export type SanitizerFn = (value: any) => string; -/** - * Directive and element indices for top-level directive. - * - * Saved here to avoid re-instantiating an array on every change detection run. - * - * Note: Element is not actually stored at index 0 because of the LViewData - * header, but the host bindings function expects an index that is NOT adjusted - * because it will ultimately be fed to instructions like elementProperty. - */ -const _ROOT_DIRECTIVE_INDICES = [0, 0]; - /** * TView.data needs to fill the same number of slots as the LViewData header * so the indices of nodes are consistent between LViewData and TView.data. @@ -346,7 +335,7 @@ export function setHostBindings(bindings: number[] | null): void { for (let i = 0; i < bindings.length; i += 2) { const dirIndex = bindings[i]; const def = defs[dirIndex] as DirectiveDefInternal; - def.hostBindings && def.hostBindings(dirIndex, bindings[i + 1]); + def.hostBindings !(dirIndex, bindings[i + 1]); bindingRootIndex = viewData[BINDING_INDEX] = bindingRootIndex + def.hostVars; } } @@ -383,26 +372,14 @@ export function executeInitAndContentHooks(): void { export function createLViewData( renderer: Renderer3, tView: TView, context: T | null, flags: LViewFlags, sanitizer?: Sanitizer | null): LViewData { - // TODO(kara): create from blueprint - return [ - tView, // tView - viewData, // parent - null, // next - null, // queries - flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.RunInit, // flags - null !, // hostNode - tView.bindingStartIndex, // bindingIndex - null, // directives - null, // cleanupInstances - context, // context - viewData ? viewData[INJECTOR] : null, // injector - renderer, // renderer - sanitizer || null, // sanitizer - null, // tail - -1, // containerIndex - null, // contentQueries - null // declarationView - ]; + const instance = tView.blueprint.slice() as LViewData; + instance[PARENT] = viewData; + instance[FLAGS] = flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.RunInit; + instance[CONTEXT] = context; + instance[INJECTOR] = viewData ? viewData[INJECTOR] : null; + instance[RENDERER] = renderer; + instance[SANITIZER] = sanitizer || null; + return instance; } /** @@ -474,13 +451,13 @@ export function createLNode( const adjustedIndex = index + HEADER_OFFSET; // This is an element or container or projection node - ngDevMode && assertDataNext(adjustedIndex); const tData = tView.data; + ngDevMode && assertLessThan( + adjustedIndex, viewData.length, `Slot should have been initialized with null`); viewData[adjustedIndex] = node; - // Every node adds a value to the static data array to avoid a sparse array - if (adjustedIndex >= tData.length) { + if (tData[adjustedIndex] == null) { const tNode = tData[adjustedIndex] = createTNode(type, adjustedIndex, name, attrs, tParent, null); if (!isParent && previousOrParentNode) { @@ -504,8 +481,9 @@ export function createLNode( // View nodes and host elements need to set their host node (components set host nodes later) if ((type & TNodeType.ViewOrElement) === TNodeType.ViewOrElement && isState) { const lViewData = state as LViewData; - ngDevMode && assertNotDefined( - lViewData[HOST_NODE], 'lViewData[HOST_NODE] should not have been initialized'); + ngDevMode && + assertEqual( + lViewData[HOST_NODE], null, 'lViewData[HOST_NODE] should not have been initialized'); lViewData[HOST_NODE] = node; if (firstTemplatePass) lViewData[TVIEW].node = node.tNode; } @@ -515,6 +493,20 @@ export function createLNode( return node; } +/** + * When LNodes are created dynamically after a view blueprint is created (e.g. through + * i18nApply() or ComponentFactory.create), we need to adjust the blueprint for future + * template passes. + */ +export function adjustBlueprintForNewNode(view: LViewData) { + const tView = view[TVIEW]; + if (tView.firstTemplatePass) { + tView.hostBindingStartIndex++; + tView.blueprint.push(null); + view.push(null); + } +} + ////////////////////////// //// Render @@ -664,7 +656,7 @@ export function renderComponentOrTemplate( // Element was stored at 0 in data and directive was stored at 0 in directives // in renderComponent() - setHostBindings(_ROOT_DIRECTIVE_INDICES); + setHostBindings(tView.hostBindings); componentRefresh(HEADER_OFFSET); } } finally { @@ -910,19 +902,23 @@ export function resolveDirective( /** Stores index of component's host element so it will be queued for view refresh during CD. */ function queueComponentIndexForCheck(): void { if (firstTemplatePass) { - (tView.components || (tView.components = [])).push(viewData.length - 1); + (tView.components || (tView.components = [])).push(previousOrParentNode.tNode.index); } } /** Stores index of directive and host element so it will be queued for binding refresh during CD. */ -function queueHostBindingForCheck(dirIndex: number): void { +export function queueHostBindingForCheck(dirIndex: number, hostVars: number): void { // Must subtract the header offset because hostBindings functions are generated with // instructions that expect element indices that are NOT adjusted (e.g. elementProperty). ngDevMode && assertEqual(firstTemplatePass, true, 'Should only be called in first template pass.'); + for (let i = 0; i < hostVars; i++) { + tView.blueprint.push(NO_CHANGE); + viewData.push(NO_CHANGE); + } (tView.hostBindings || (tView.hostBindings = [ - ])).push(dirIndex, viewData.length - 1 - HEADER_OFFSET); + ])).push(dirIndex, previousOrParentNode.tNode.index - HEADER_OFFSET); } /** Sets the context for a ChangeDetectorRef to the given instance. */ @@ -1005,10 +1001,11 @@ function saveResolvedLocalsInData( lNode: LNodeWithLocalRefs, localRefExtractor: LocalRefExtractor): void { const localNames = lNode.tNode.localNames; if (localNames) { + let localIndex = lNode.tNode.index + 1; for (let i = 0; i < localNames.length; i += 2) { const index = localNames[i + 1] as number; const value = index === -1 ? localRefExtractor(lNode) : directives ![index]; - viewData.push(value); + viewData[localIndex++] = value; } } } @@ -1055,15 +1052,21 @@ export function createTView( viewQuery: ComponentQuery| null): TView { ngDevMode && ngDevMode.tView++; const bindingStartIndex = HEADER_OFFSET + consts; - return { + // This length does not yet contain host bindings from child directives because at this point, + // we don't know which directives are active on this template. As soon as a directive is matched + // that has a host binding, we will update the blueprint with that def's hostVars count. + const initialViewLength = bindingStartIndex + vars; + const blueprint = createViewBlueprint(bindingStartIndex, initialViewLength); + return blueprint[TVIEW] = { id: viewIndex, + blueprint: blueprint, template: templateFn, viewQuery: viewQuery, node: null !, data: HEADER_FILLER.slice(), // Fill in to match HEADER_OFFSET in LViewData childIndex: -1, // Children set in addToViewTree(), if any bindingStartIndex: bindingStartIndex, - hostBindingStartIndex: bindingStartIndex + vars, + hostBindingStartIndex: initialViewLength, directives: null, firstTemplatePass: true, initHooks: null, @@ -1084,6 +1087,15 @@ export function createTView( }; } +function createViewBlueprint(bindingStartIndex: number, initialViewLength: number): LViewData { + const blueprint = new Array(initialViewLength) + .fill(null, 0, bindingStartIndex) + .fill(NO_CHANGE, bindingStartIndex) as LViewData; + blueprint[CONTAINER_INDEX] = -1; + blueprint[BINDING_INDEX] = bindingStartIndex; + return blueprint; +} + function setUpAttributes(native: RElement, attrs: TAttributes): void { const isProc = isProceduralRenderer(renderer); let i = 0; @@ -1680,7 +1692,7 @@ export function directiveCreate( // any projected components. queueInitHooks(directiveDefIdx, directiveDef.onInit, directiveDef.doCheck, tView); - if (directiveDef.hostBindings) queueHostBindingForCheck(directiveDefIdx); + if (directiveDef.hostBindings) queueHostBindingForCheck(directiveDefIdx, directiveDef.hostVars); } if (tNode && tNode.attrs) { @@ -2758,8 +2770,10 @@ export function getBinding(bindingIndex: number): any { /** Updates binding if changed, then returns whether it was updated. */ export function bindingUpdated(bindingIndex: number, value: any): boolean { ngDevMode && assertNotEqual(value, NO_CHANGE, 'Incoming value should never be NO_CHANGE.'); + ngDevMode && assertLessThan( + bindingIndex, viewData.length, `Slot should have been initialized to NO_CHANGE`); - if (bindingIndex >= viewData.length) { + if (viewData[bindingIndex] === NO_CHANGE) { viewData[bindingIndex] = value; } else if (isDifferent(viewData[bindingIndex], value, checkNoChangesMode)) { throwErrorIfNoChangesMode(creationMode, checkNoChangesMode, viewData[bindingIndex], value); @@ -2843,4 +2857,3 @@ export function _getComponentHostLElementNode(component: T): LElementNode { } export const CLEAN_PROMISE = _CLEAN_PROMISE; -export const ROOT_DIRECTIVE_INDICES = _ROOT_DIRECTIVE_INDICES; diff --git a/packages/core/src/render3/interfaces/view.ts b/packages/core/src/render3/interfaces/view.ts index ce0246a58b..136bccf3f7 100644 --- a/packages/core/src/render3/interfaces/view.ts +++ b/packages/core/src/render3/interfaces/view.ts @@ -253,6 +253,12 @@ export interface TView { */ readonly id: number; + /** + * This is a blueprint used to generate LViewData instances for this TView. Copying this + * blueprint is faster than creating a new LViewData from scratch. + */ + blueprint: LViewData; + /** * The template function used to refresh the view of dynamically created views * and components. Will be null for inline views. diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json index af4b06f2ff..e020817d97 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -56,6 +56,9 @@ { "name": "NG_PROJECT_AS_ATTR_NAME" }, + { + "name": "NO_CHANGE" + }, { "name": "PARENT" }, @@ -72,7 +75,7 @@ "name": "RENDER_PARENT" }, { - "name": "ROOT_DIRECTIVE_INDICES" + "name": "SANITIZER" }, { "name": "TVIEW" @@ -83,9 +86,6 @@ { "name": "_CLEAN_PROMISE" }, - { - "name": "_ROOT_DIRECTIVE_INDICES" - }, { "name": "_getComponentHostLElementNode" }, @@ -143,6 +143,9 @@ { "name": "createTextNode" }, + { + "name": "createViewBlueprint" + }, { "name": "createViewQuery" }, @@ -239,6 +242,9 @@ { "name": "nextNgElementId" }, + { + "name": "queueHostBindingForCheck" + }, { "name": "readElementValue" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 5c5da9df29..275423dc22 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -131,9 +131,6 @@ { "name": "RENDER_PARENT" }, - { - "name": "ROOT_DIRECTIVE_INDICES" - }, { "name": "RecordViewTuple" }, @@ -203,9 +200,6 @@ { "name": "_DuplicateMap" }, - { - "name": "_ROOT_DIRECTIVE_INDICES" - }, { "name": "_THROW_IF_NOT_FOUND" }, @@ -413,6 +407,9 @@ { "name": "createTextNode" }, + { + "name": "createViewBlueprint" + }, { "name": "createViewQuery" }, diff --git a/packages/core/test/render3/change_detection_spec.ts b/packages/core/test/render3/change_detection_spec.ts index 596b281a0b..6c0bb46a68 100644 --- a/packages/core/test/render3/change_detection_spec.ts +++ b/packages/core/test/render3/change_detection_spec.ts @@ -105,7 +105,7 @@ describe('change detection', () => { selectors: [['my-comp']], factory: () => comp = new MyComponent(), consts: 2, - vars: 1, + vars: 2, /** * {{ doCheckCount }} - {{ name }} * diff --git a/packages/core/test/render3/common_integration_spec.ts b/packages/core/test/render3/common_integration_spec.ts index fb942d01bc..8796872d98 100644 --- a/packages/core/test/render3/common_integration_spec.ts +++ b/packages/core/test/render3/common_integration_spec.ts @@ -109,7 +109,7 @@ describe('@angular/common integration', () => { template: (rf: RenderFlags, ctx: MyApp) => { if (rf & RenderFlags.Create) { elementStart(0, 'ul'); - { template(1, liTemplate, 2, 1, undefined, ['ngForOf', '']); } + { template(1, liTemplate, 2, 3, undefined, ['ngForOf', '']); } elementEnd(); } if (rf & RenderFlags.Update) { @@ -246,7 +246,7 @@ describe('@angular/common integration', () => { function liTemplate(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'li'); - { template(1, spanTemplate, 2, 1, null, ['ngForOf', '']); } + { template(1, spanTemplate, 2, 3, null, ['ngForOf', '']); } elementEnd(); } if (rf & RenderFlags.Update) { @@ -335,7 +335,7 @@ describe('@angular/common integration', () => { function divTemplate(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'div'); - { template(1, pTemplate, 3, 1, null, ['ngForOf', '']); } + { template(1, pTemplate, 3, 2, null, ['ngForOf', '']); } elementEnd(); } if (rf & RenderFlags.Update) { @@ -441,7 +441,7 @@ describe('@angular/common integration', () => { function innerDivTemplate(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'div'); - { template(1, spanTemplate, 2, 1, null, ['ngForOf', '']); } + { template(1, spanTemplate, 2, 2, null, ['ngForOf', '']); } elementEnd(); } if (rf & RenderFlags.Update) { @@ -681,7 +681,7 @@ describe('@angular/common integration', () => { function itemTemplate7(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'span'); - { template(1, itemTemplate8, 2, 1, null, ['ngForOf', '']); } + { template(1, itemTemplate8, 2, 10, null, ['ngForOf', '']); } elementEnd(); } if (rf & RenderFlags.Update) { diff --git a/packages/core/test/render3/compiler_canonical/component_directives_spec.ts b/packages/core/test/render3/compiler_canonical/component_directives_spec.ts index e8a8f1c896..680a78b988 100644 --- a/packages/core/test/render3/compiler_canonical/component_directives_spec.ts +++ b/packages/core/test/render3/compiler_canonical/component_directives_spec.ts @@ -103,6 +103,7 @@ describe('components & directives', () => { type: HostBindingDir, selectors: [['', 'hostBindingDir', '']], factory: function HostBindingDir_Factory() { return new HostBindingDir(); }, + hostVars: 1, hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) { $r3$.ɵelementProperty( elIndex, 'id', $r3$.ɵbind($r3$.ɵloadDirective(dirIndex).dirId)); @@ -256,6 +257,7 @@ describe('components & directives', () => { type: HostBindingDir, selectors: [['', 'hostBindingDir', '']], factory: function HostBindingDir_Factory() { return new HostBindingDir(); }, + hostVars: 1, hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) { $r3$.ɵelementAttribute( elIndex, 'aria-label', @@ -446,7 +448,7 @@ describe('components & directives', () => { selectors: [['my-array-comp']], factory: function MyArrayComp_Factory() { return new MyArrayComp(); }, consts: 1, - vars: 1, + vars: 2, template: function MyArrayComp_Template(rf: $RenderFlags$, ctx: $MyArrayComp$) { if (rf & 1) { $r3$.ɵtext(0); @@ -479,7 +481,7 @@ describe('components & directives', () => { selectors: [['my-app']], factory: function MyApp_Factory() { return new MyApp(); }, consts: 1, - vars: 1, + vars: 0, template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) { if (rf & 1) { $r3$.ɵelement(0, 'my-array-comp'); diff --git a/packages/core/test/render3/compiler_canonical/elements_spec.ts b/packages/core/test/render3/compiler_canonical/elements_spec.ts index 1157b3fdc3..2a9ccecd20 100644 --- a/packages/core/test/render3/compiler_canonical/elements_spec.ts +++ b/packages/core/test/render3/compiler_canonical/elements_spec.ts @@ -91,7 +91,7 @@ describe('elements', () => { selectors: [['local-ref-comp']], factory: function LocalRefComp_Factory() { return new LocalRefComp(); }, consts: 4, - vars: 1, + vars: 2, template: function LocalRefComp_Template(rf: $RenderFlags$, ctx: $LocalRefComp$) { if (rf & 1) { $r3$.ɵelement(0, 'div', $e0_attrs$, $e0_locals$); @@ -135,7 +135,7 @@ describe('elements', () => { type: ListenerComp, selectors: [['listener-comp']], factory: function ListenerComp_Factory() { return new ListenerComp(); }, - consts: 1, + consts: 2, vars: 0, template: function ListenerComp_Template(rf: $RenderFlags$, ctx: $ListenerComp$) { if (rf & 1) { diff --git a/packages/core/test/render3/component_spec.ts b/packages/core/test/render3/component_spec.ts index d6c355959a..4a8044e88a 100644 --- a/packages/core/test/render3/component_spec.ts +++ b/packages/core/test/render3/component_spec.ts @@ -276,7 +276,7 @@ describe('encapsulation', () => { static ngComponentDef = defineComponent({ type: LeafComponentwith, selectors: [['leaf']], - consts: 1, + consts: 2, vars: 0, template: function(rf: RenderFlags, ctx: LeafComponentwith) { if (rf & RenderFlags.Create) { diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts index 1522c6fde7..d64a369751 100644 --- a/packages/core/test/render3/di_spec.ts +++ b/packages/core/test/render3/di_spec.ts @@ -828,7 +828,7 @@ describe('di', () => { const tmp2 = reference(2) as any; textBinding(3, interpolation2('', tmp2.value, '-', tmp1.value, '')); } - }, 4, 1, [Directive, DirectiveSameInstance]); + }, 4, 2, [Directive, DirectiveSameInstance]); const fixture = new ComponentFixture(App); expect(fixture.html).toEqual('
ElementRef-true
'); @@ -872,7 +872,7 @@ describe('di', () => { const App = createComponent('app', function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { template(0, function() { - }, 0, 1, undefined, ['dir', '', 'dirSame', ''], ['dir', 'dir', 'dirSame', 'dirSame']); + }, 0, 0, undefined, ['dir', '', 'dirSame', ''], ['dir', 'dir', 'dirSame', 'dirSame']); text(3); } if (rf & RenderFlags.Update) { @@ -880,7 +880,7 @@ describe('di', () => { const tmp2 = reference(2) as any; textBinding(3, interpolation2('', tmp1.value, '-', tmp2.value, '')); } - }, 4, 1, [Directive, DirectiveSameInstance]); + }, 4, 2, [Directive, DirectiveSameInstance]); const fixture = new ComponentFixture(App); expect(fixture.html).toEqual('TemplateRef-true'); @@ -933,7 +933,7 @@ describe('di', () => { const tmp2 = reference(2) as any; textBinding(3, interpolation2('', tmp1.value, '-', tmp2.value, '')); } - }, 4, 1, [Directive, DirectiveSameInstance]); + }, 4, 2, [Directive, DirectiveSameInstance]); const fixture = new ComponentFixture(App); expect(fixture.html).toEqual('
ViewContainerRef-true
'); @@ -1217,7 +1217,7 @@ describe('di', () => { exist = injectAttribute('exist'); nonExist = injectAttribute('nonExist'); } - }); + }, 1); new ComponentFixture(MyApp); expect(exist).toEqual('existValue'); @@ -1376,7 +1376,7 @@ describe('di', () => { if (rf & RenderFlags.Update) { containerRefreshStart(1); { - let rf1 = embeddedViewStart(0, 4, 1); + let rf1 = embeddedViewStart(0, 4, 2); if (rf1 & RenderFlags.Create) { elementStart( 0, 'span', ['childDir', '', 'child2Dir', ''], @@ -1408,7 +1408,7 @@ describe('di', () => { describe('getOrCreateNodeInjector', () => { it('should handle initial undefined state', () => { const contentView = createLViewData( - null !, createTView(-1, null, 0, 0, null, null, null), null, LViewFlags.CheckAlways); + null !, createTView(-1, null, 1, 0, null, null, null), null, LViewFlags.CheckAlways); const oldView = enterView(contentView, null !); try { const parent = createLNode(0, TNodeType.Element, null, null, null, null); diff --git a/packages/core/test/render3/exports_spec.ts b/packages/core/test/render3/exports_spec.ts index 3b01ef29a2..023d956b83 100644 --- a/packages/core/test/render3/exports_spec.ts +++ b/packages/core/test/render3/exports_spec.ts @@ -365,7 +365,7 @@ describe('exports', () => { if (rf & RenderFlags.Create) { elementStart(0, 'input', ['value', 'one'], ['outerInput', '']); elementEnd(); - template(2, outerTemplate, 5, 0, '', [AttributeMarker.SelectOnly, 'ngIf']); + template(2, outerTemplate, 5, 2, '', [AttributeMarker.SelectOnly, 'ngIf']); } if (rf & RenderFlags.Update) { elementProperty(2, 'ngIf', bind(app.outer)); @@ -379,7 +379,7 @@ describe('exports', () => { text(1); elementStart(2, 'input', ['value', 'two'], ['innerInput', '']); elementEnd(); - template(4, innerTemplate, 2, 1, '', [AttributeMarker.SelectOnly, 'ngIf']); + template(4, innerTemplate, 2, 2, '', [AttributeMarker.SelectOnly, 'ngIf']); } elementEnd(); } diff --git a/packages/core/test/render3/i18n_spec.ts b/packages/core/test/render3/i18n_spec.ts index 8b29b6025e..4038de3f17 100644 --- a/packages/core/test/render3/i18n_spec.ts +++ b/packages/core/test/render3/i18n_spec.ts @@ -196,7 +196,7 @@ describe('Runtime i18n', () => { factory: () => new MyApp(), selectors: [['my-app']], consts: 1, - vars: 1, + vars: 2, // Initial template: //
@@ -1199,7 +1199,7 @@ describe('Runtime i18n', () => { selectors: [['parent']], directives: [Child], factory: () => new Parent(), - consts: 4, + consts: 7, vars: 2, template: (rf: RenderFlags, cmp: Parent) => { if (rf & RenderFlags.Create) { @@ -1512,7 +1512,7 @@ describe('Runtime i18n', () => { factory: () => new MyApp(), selectors: [['my-app']], consts: 1, - vars: 1, + vars: 4, // Initial template: //
@@ -1550,7 +1550,7 @@ describe('Runtime i18n', () => { factory: () => new MyApp(), selectors: [['my-app']], consts: 1, - vars: 1, + vars: 3, // Initial template: //
@@ -1587,7 +1587,7 @@ describe('Runtime i18n', () => { factory: () => new MyApp(), selectors: [['my-app']], consts: 1, - vars: 1, + vars: 4, // Initial template: //
@@ -1626,7 +1626,7 @@ describe('Runtime i18n', () => { factory: () => new MyApp(), selectors: [['my-app']], consts: 1, - vars: 1, + vars: 5, // Initial template: //
@@ -1670,7 +1670,7 @@ describe('Runtime i18n', () => { factory: () => new MyApp(), selectors: [['my-app']], consts: 1, - vars: 1, + vars: 6, // Initial template: //
@@ -1716,7 +1716,7 @@ describe('Runtime i18n', () => { factory: () => new MyApp(), selectors: [['my-app']], consts: 1, - vars: 1, + vars: 7, // Initial template: //
@@ -1771,7 +1771,7 @@ describe('Runtime i18n', () => { factory: () => new MyApp(), selectors: [['my-app']], consts: 1, - vars: 1, + vars: 8, // Initial template: //
diff --git a/packages/core/test/render3/instructions_spec.ts b/packages/core/test/render3/instructions_spec.ts index 40daba5a14..f7e485702c 100644 --- a/packages/core/test/render3/instructions_spec.ts +++ b/packages/core/test/render3/instructions_spec.ts @@ -40,7 +40,7 @@ describe('instructions', () => { describe('bind', () => { it('should update bindings when value changes', () => { - const t = new TemplateFixture(createAnchor, () => {}, 1); + const t = new TemplateFixture(createAnchor, () => {}, 1, 1); t.update(() => elementProperty(0, 'title', bind('Hello'))); expect(t.html).toEqual(''); diff --git a/packages/core/test/render3/integration_spec.ts b/packages/core/test/render3/integration_spec.ts index 41621a2707..7d4e78687d 100644 --- a/packages/core/test/render3/integration_spec.ts +++ b/packages/core/test/render3/integration_spec.ts @@ -340,6 +340,7 @@ describe('render3 integration test', () => { } }, factory: () => cmptInstance = new TodoComponentHostBinding, + hostVars: 1, hostBindings: function(directiveIndex: number, elementIndex: number): void { // host bindings elementProperty( @@ -930,7 +931,7 @@ describe('render3 integration test', () => { containerRefreshStart(1); { for (let subTree of ctx.tree.subTrees || []) { - const rf0 = embeddedViewStart(0, 1, 0); + const rf0 = embeddedViewStart(0, 3, 0); { showTree(rf0, {tree: subTree}); } embeddedViewEnd(); } @@ -967,14 +968,14 @@ describe('render3 integration test', () => { if (rf & RenderFlags.Update) { containerRefreshStart(0); { - const rf0 = embeddedViewStart(0, 1, 0); + const rf0 = embeddedViewStart(0, 3, 0); { showTree(rf0, {tree: ctx.beforeTree}); } embeddedViewEnd(); } containerRefreshEnd(); containerRefreshStart(2); { - const rf0 = embeddedViewStart(0, 1, 0); + const rf0 = embeddedViewStart(0, 3, 0); { showTree(rf0, {tree: ctx.afterTree}); } embeddedViewEnd(); } @@ -997,7 +998,7 @@ describe('render3 integration test', () => { elementProperty(0, 'afterTree', bind(ctx.afterTree)); containerRefreshStart(1); { - const rf0 = embeddedViewStart(0, 1, 0); + const rf0 = embeddedViewStart(0, 3, 0); { showTree(rf0, {tree: ctx.projectedTree}); } embeddedViewEnd(); } @@ -1109,15 +1110,15 @@ describe('render3 integration test', () => { } } let args = ['(', 0, 'a', 1, 'b', 2, 'c', 3, 'd', 4, 'e', 5, 'f', 6, 'g', 7, ')']; - expect(renderToHtml(Template, args, 1, 10)) + expect(renderToHtml(Template, args, 1, 54)) .toEqual( ''); args = args.reverse(); - expect(renderToHtml(Template, args, 1, 10)) + expect(renderToHtml(Template, args, 1, 54)) .toEqual( ''); args = args.reverse(); - expect(renderToHtml(Template, args, 1, 10)) + expect(renderToHtml(Template, args, 1, 54)) .toEqual( ''); }); @@ -1136,14 +1137,16 @@ describe('render3 integration test', () => { containerRefreshStart(1); { if (true) { - let rf1 = embeddedViewStart(1, 1, 0); + let rf1 = embeddedViewStart(1, 1, 1); { if (rf1 & RenderFlags.Create) { elementStart(0, 'b'); {} elementEnd(); } - elementAttribute(0, 'title', bind(ctx.title)); + if (rf1 & RenderFlags.Update) { + elementAttribute(0, 'title', bind(ctx.title)); + } } embeddedViewEnd(); } @@ -1182,6 +1185,7 @@ describe('render3 integration test', () => { factory: function HostBindingDir_Factory() { return hostBindingDir = new HostBindingDir(); }, + hostVars: 1, hostBindings: function HostBindingDir_HostBindings(dirIndex: number, elIndex: number) { elementAttribute( elIndex, 'aria-label', bind(loadDirective(dirIndex).label)); diff --git a/packages/core/test/render3/lifecycle_spec.ts b/packages/core/test/render3/lifecycle_spec.ts index 839136e9b1..b011d0bf18 100644 --- a/packages/core/test/render3/lifecycle_spec.ts +++ b/packages/core/test/render3/lifecycle_spec.ts @@ -41,7 +41,7 @@ describe('lifecycles', () => { elementEnd(); } }, 2); - let Parent = createOnInitComponent('parent', getParentTemplate('comp'), 1, [Comp]); + let Parent = createOnInitComponent('parent', getParentTemplate('comp'), 1, 1, [Comp]); let ProjectedComp = createOnInitComponent('projected', (rf: RenderFlags, ctx: any) => { if (rf & RenderFlags.Create) { text(0, 'content'); @@ -49,7 +49,8 @@ describe('lifecycles', () => { }, 1); function createOnInitComponent( - name: string, template: ComponentTemplate, consts: number, directives: any[] = []) { + name: string, template: ComponentTemplate, consts: number, vars: number = 0, + directives: any[] = []) { return class Component { val: string = ''; ngOnInit() { @@ -61,7 +62,7 @@ describe('lifecycles', () => { type: Component, selectors: [[name]], consts: consts, - vars: 0, + vars: vars, factory: () => new Component(), inputs: {val: 'val'}, template, directives: directives diff --git a/packages/core/test/render3/pipe_spec.ts b/packages/core/test/render3/pipe_spec.ts index af1ed3db80..360af2a2ef 100644 --- a/packages/core/test/render3/pipe_spec.ts +++ b/packages/core/test/render3/pipe_spec.ts @@ -236,7 +236,7 @@ describe('pipe', () => { containerRefreshStart(4); { for (let i of [1, 2]) { - let rf1 = embeddedViewStart(1, 2, 0); + let rf1 = embeddedViewStart(1, 2, 3); { if (rf1 & RenderFlags.Create) { elementStart(0, 'div'); diff --git a/packages/core/test/render3/pure_function_spec.ts b/packages/core/test/render3/pure_function_spec.ts index b4897bdba8..f01c0f3548 100644 --- a/packages/core/test/render3/pure_function_spec.ts +++ b/packages/core/test/render3/pure_function_spec.ts @@ -510,7 +510,7 @@ describe('object literals', () => { containerRefreshStart(0); { for (let i = 0; i < 2; i++) { - let rf1 = embeddedViewStart(0, 1, 0); + let rf1 = embeddedViewStart(0, 1, 4); if (rf1 & RenderFlags.Create) { elementStart(0, 'object-comp'); objectComps.push(loadDirective(0)); @@ -531,12 +531,12 @@ describe('object literals', () => { const e0_ff = (v1: any, v2: any) => { return {opacity: v1, duration: v2}; }; const configs = [{opacity: 0, duration: 500}, {opacity: 1, duration: 600}]; - renderToHtml(Template, {configs}, 1, 4, defs); + renderToHtml(Template, {configs}, 1, 0, defs); expect(objectComps[0].config).toEqual({opacity: 0, duration: 500}); expect(objectComps[1].config).toEqual({opacity: 1, duration: 600}); configs[0].duration = 1000; - renderToHtml(Template, {configs}, 1, 4, defs); + renderToHtml(Template, {configs}, 1, 0, defs); expect(objectComps[0].config).toEqual({opacity: 0, duration: 1000}); expect(objectComps[1].config).toEqual({opacity: 1, duration: 600}); }); diff --git a/packages/core/test/render3/query_spec.ts b/packages/core/test/render3/query_spec.ts index 0bbb52be60..defe635c9b 100644 --- a/packages/core/test/render3/query_spec.ts +++ b/packages/core/test/render3/query_spec.ts @@ -1148,7 +1148,7 @@ describe('query', () => { } }, - 7, 0, [ViewContainerManipulatorDirective], [], + 9, 0, [ViewContainerManipulatorDirective], [], function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { query(0, ['foo'], true, QUERY_READ_FROM_NODE); diff --git a/packages/core/test/render3/render_util.ts b/packages/core/test/render3/render_util.ts index c0c720c207..3351140da8 100644 --- a/packages/core/test/render3/render_util.ts +++ b/packages/core/test/render3/render_util.ts @@ -89,7 +89,7 @@ export class TemplateFixture extends BaseFixture { */ update(updateBlock?: () => void): void { renderTemplate( - this.hostNode.native, updateBlock || this.updateBlock, 0, null !, this.vars, + this.hostNode.native, updateBlock || this.updateBlock, 0, this.vars, null !, this._rendererFactory, this.hostNode, this._directiveDefs, this._pipeDefs, this._sanitizer); } } diff --git a/packages/core/test/render3/styling_spec.ts b/packages/core/test/render3/styling_spec.ts index 46415d86ba..e84893aedd 100644 --- a/packages/core/test/render3/styling_spec.ts +++ b/packages/core/test/render3/styling_spec.ts @@ -166,12 +166,12 @@ describe('styling', () => { } } - expect(renderToHtml(Template, { - myStyles: {width: '200px', height: '200px'}, - myWidth: '300px' - })).toEqual(''); + expect(renderToHtml( + Template, {myStyles: {width: '200px', height: '200px'}, myWidth: '300px'}, 1)) + .toEqual(''); - expect(renderToHtml(Template, {myStyles: {width: '200px', height: null}, myWidth: null})) + expect( + renderToHtml(Template, {myStyles: {width: '200px', height: null}, myWidth: null}, 1)) .toEqual(''); }); }); diff --git a/packages/core/test/render3/view_container_ref_spec.ts b/packages/core/test/render3/view_container_ref_spec.ts index be18553e9d..617b10e1b7 100644 --- a/packages/core/test/render3/view_container_ref_spec.ts +++ b/packages/core/test/render3/view_container_ref_spec.ts @@ -123,7 +123,7 @@ describe('ViewContainerRef', () => { } const fixture = new TemplateFixture( - createTemplate, updateTemplate, 3, 1, [HeaderComponent, DirectiveWithVCRef]); + createTemplate, updateTemplate, 4, 1, [HeaderComponent, DirectiveWithVCRef]); expect(fixture.html).toEqual(''); createView('A'); @@ -643,7 +643,7 @@ describe('ViewContainerRef', () => { function rowTemplate(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - template(0, cellTemplate, 2, 1, null, null, ['cellTemplate', ''], templateRefExtractor); + template(0, cellTemplate, 2, 3, null, null, ['cellTemplate', ''], templateRefExtractor); element(2, 'loop-comp'); }