diff --git a/packages/core/src/render3/assert.ts b/packages/core/src/render3/assert.ts index e7e0bcfe89..10644d102b 100644 --- a/packages/core/src/render3/assert.ts +++ b/packages/core/src/render3/assert.ts @@ -70,6 +70,7 @@ export function assertLView(value: any) { assertEqual(isLView(value), true, 'Expecting LView'); } -export function assertFirstTemplatePass(tView: TView, errMessage: string) { - assertEqual(tView.firstTemplatePass, true, errMessage); +export function assertFirstTemplatePass(tView: TView, errMessage?: string) { + assertEqual( + tView.firstTemplatePass, true, errMessage || 'Should only be called in first template pass.'); } diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index 7a4cb2e30e..8d380e3c74 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -17,9 +17,9 @@ import {assertComponentType} from './assert'; import {getComponentDef} from './definition'; import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di'; import {registerPostOrderHooks, registerPreOrderHooks} from './hooks'; -import {CLEAN_PROMISE, addToViewTree, createLView, createTView, getOrCreateTNode, getOrCreateTView, initNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, queueComponentIndexForCheck, refreshDescendantViews} from './instructions/shared'; +import {CLEAN_PROMISE, addToViewTree, createLView, createTView, getOrCreateTNode, getOrCreateTView, initNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, markAsComponentHost, refreshDescendantViews} from './instructions/shared'; import {ComponentDef, ComponentType, RenderFlags} from './interfaces/definition'; -import {TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node'; +import {TElementNode, TNode, TNodeType} from './interfaces/node'; import {PlayerHandler} from './interfaces/player'; import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; import {CONTEXT, FLAGS, HEADER_OFFSET, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view'; @@ -184,9 +184,8 @@ export function createRootComponentView( if (tView.firstTemplatePass) { diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, rootView), tView, def.type); - tNode.flags = TNodeFlags.isComponent; + markAsComponentHost(tView, tNode); initNodeFlags(tNode, rootView.length, 1); - queueComponentIndexForCheck(tNode); } // Store component view at node index, with node as the HOST diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index 0aa8b3953f..1fe39d904b 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -14,7 +14,7 @@ import {Sanitizer} from '../../sanitization/security'; import {assertDataInRange, assertDefined, assertDomNode, assertEqual, assertGreaterThan, assertNotEqual, assertNotSame} from '../../util/assert'; import {createNamedArrayType} from '../../util/named_array_type'; import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../../util/ng_reflect'; -import {assertLView, assertPreviousIsParent} from '../assert'; +import {assertFirstTemplatePass, assertLView, assertPreviousIsParent} from '../assert'; import {attachPatchData, getComponentViewByInstance} from '../context_discovery'; import {diPublicInInjector, getNodeInjectable, getOrCreateNodeInjectorForNode} from '../di'; import {throwMultipleComponentError} from '../errors'; @@ -1207,7 +1207,7 @@ function postProcessBaseDirective( function findDirectiveMatches( tView: TView, viewData: LView, tNode: TElementNode | TContainerNode | TElementContainerNode): DirectiveDef[]|null { - ngDevMode && assertEqual(tView.firstTemplatePass, true, 'should run on first template pass only'); + ngDevMode && assertFirstTemplatePass(tView); const registry = tView.directiveRegistry; let matches: any[]|null = null; if (registry) { @@ -1219,8 +1219,7 @@ function findDirectiveMatches( if (isComponentDef(def)) { if (tNode.flags & TNodeFlags.isComponent) throwMultipleComponentError(tNode); - tNode.flags = TNodeFlags.isComponent; - + markAsComponentHost(tView, tNode); // The component is always stored first with directives after. matches.unshift(def); } else { @@ -1232,13 +1231,16 @@ function findDirectiveMatches( return matches; } -/** Stores index of component's host element so it will be queued for view refresh during CD. */ -export function queueComponentIndexForCheck(previousOrParentTNode: TNode): void { - const tView = getLView()[TVIEW]; - ngDevMode && - assertEqual(tView.firstTemplatePass, true, 'Should only be called in first template pass.'); +/** + * Marks a given TNode as a component's host. This consists of: + * - setting appropriate TNode flags; + * - storing index of component's host element so it will be queued for view refresh during CD. +*/ +export function markAsComponentHost(tView: TView, hostTNode: TNode): void { + ngDevMode && assertFirstTemplatePass(tView); + hostTNode.flags = TNodeFlags.isComponent; (tView.components || (tView.components = ngDevMode ? new TViewComponents !() : [ - ])).push(previousOrParentTNode.index); + ])).push(hostTNode.index); } @@ -1306,10 +1308,8 @@ function baseResolveDirective( viewData.push(nodeInjectorFactory); } -function addComponentLogic( - lView: LView, previousOrParentTNode: TNode, def: ComponentDef): void { - const native = getNativeByTNode(previousOrParentTNode, lView); - +function addComponentLogic(lView: LView, hostTNode: TNode, def: ComponentDef): void { + const native = getNativeByTNode(hostTNode, lView); const tView = getOrCreateTView(def); // Only component views should be added to the view tree directly. Embedded views are @@ -1318,18 +1318,14 @@ function addComponentLogic( const componentView = addToViewTree( lView, createLView( lView, tView, null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, - lView[previousOrParentTNode.index], previousOrParentTNode as TElementNode, - rendererFactory, rendererFactory.createRenderer(native as RElement, def))); + lView[hostTNode.index], hostTNode as TElementNode, rendererFactory, + rendererFactory.createRenderer(native as RElement, def))); - componentView[T_HOST] = previousOrParentTNode as TElementNode; + componentView[T_HOST] = hostTNode as TElementNode; // Component view will always be created before any injected LContainers, // so this is a regular element, wrap it with the component view - lView[previousOrParentTNode.index] = componentView; - - if (lView[TVIEW].firstTemplatePass) { - queueComponentIndexForCheck(previousOrParentTNode); - } + lView[hostTNode.index] = componentView; } export function elementAttributeInternal( diff --git a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json index d62b81cd9a..e8b3085865 100644 --- a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json +++ b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json @@ -515,6 +515,9 @@ { "name": "locateHostElement" }, + { + "name": "markAsComponentHost" + }, { "name": "matchTemplateAttribute" }, @@ -545,9 +548,6 @@ { "name": "postProcessDirective" }, - { - "name": "queueComponentIndexForCheck" - }, { "name": "readPatchedData" }, 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 475281af8b..d304ab9a1a 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -386,6 +386,9 @@ { "name": "locateHostElement" }, + { + "name": "markAsComponentHost" + }, { "name": "namespaceHTMLInternal" }, @@ -410,9 +413,6 @@ { "name": "postProcessBaseDirective" }, - { - "name": "queueComponentIndexForCheck" - }, { "name": "readPatchedData" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 8bb5afa130..8323bf925f 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -1118,6 +1118,9 @@ { "name": "makeParamDecorator" }, + { + "name": "markAsComponentHost" + }, { "name": "markContextToPersistState" }, @@ -1178,9 +1181,6 @@ { "name": "postProcessDirective" }, - { - "name": "queueComponentIndexForCheck" - }, { "name": "readPatchedData" },