diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index b756e5aa6f..2c67a9232d 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -133,7 +133,7 @@ export function renderComponent( executeInitAndContentHooks(); setHostBindings(rootView[TVIEW].hostBindings); - detectChangesInternal(elementNode.data as LViewData, elementNode, component); + detectChangesInternal(elementNode.data as LViewData, component); } finally { leaveView(oldView); if (rendererFactory.end) rendererFactory.end(); diff --git a/packages/core/src/render3/context_discovery.ts b/packages/core/src/render3/context_discovery.ts index c1c0c0b5f4..ca7c6fa40c 100644 --- a/packages/core/src/render3/context_discovery.ts +++ b/packages/core/src/render3/context_discovery.ts @@ -185,10 +185,11 @@ export function getLElementNode(target: any): LElementNode|null { return context ? getLNodeFromViewData(context.lViewData, context.lNodeIndex) : null; } -export function getLElementFromRootComponent(componentInstance: {}): LElementNode|null { +export function getLElementFromRootComponent(rootComponentInstance: {}): LElementNode|null { // the host element for the root component is ALWAYS the first element // in the lViewData array (which is where HEADER_OFFSET points to) - return getLElementFromComponent(componentInstance, HEADER_OFFSET); + const lViewData = readPatchedLViewData(rootComponentInstance) !; + return readElementValue(lViewData[HEADER_OFFSET]); } /** @@ -198,15 +199,14 @@ export function getLElementFromRootComponent(componentInstance: {}): LElementNod * that `getContext` has in the event that an Angular application doesn't need to have * any programmatic access to an element's context (only change detection uses this function). */ -export function getLElementFromComponent( - componentInstance: {}, expectedLNodeIndex?: number): LElementNode|null { +export function getLElementFromComponent(componentInstance: {}): LElementNode|null { let lViewData = readPatchedData(componentInstance); let lNode: LElementNode; if (Array.isArray(lViewData)) { - expectedLNodeIndex = expectedLNodeIndex || findViaComponent(lViewData, componentInstance); - lNode = readElementValue(lViewData[expectedLNodeIndex]); - const context = createLContext(lViewData, expectedLNodeIndex, lNode.native); + const lNodeIndex = findViaComponent(lViewData, componentInstance); + lNode = readElementValue(lViewData[lNodeIndex]); + const context = createLContext(lViewData, lNodeIndex, lNode.native); context.component = componentInstance; attachPatchData(componentInstance, context); attachPatchData(context.native, context); @@ -234,6 +234,14 @@ export function readPatchedData(target: any): LViewData|LContext|null { return target[MONKEY_PATCH_KEY_NAME]; } +export function readPatchedLViewData(target: any): LViewData|null { + const value = readPatchedData(target); + if (value) { + return Array.isArray(value) ? value : (value as LContext).lViewData; + } + return null; +} + export function isComponentInstance(instance: any): boolean { return instance && instance.constructor && instance.constructor.ngComponentDef; } diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index 2eb709f339..04a11d3558 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -13,7 +13,7 @@ import {Sanitizer} from '../sanitization/security'; import {StyleSanitizeFn} from '../sanitization/style_sanitizer'; import {assertDefined, assertEqual, assertLessThan, assertNotDefined, assertNotEqual} from './assert'; -import {attachPatchData, getLElementFromComponent, getLElementFromRootComponent} from './context_discovery'; +import {attachPatchData, getLElementFromComponent, getLElementFromRootComponent, readPatchedData, readPatchedLViewData} from './context_discovery'; import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComponentError} from './errors'; import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks'; import {ACTIVE_INDEX, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container'; @@ -1769,6 +1769,9 @@ export function baseDirectiveCreate( ngDevMode && assertPreviousIsParent(); attachPatchData(directive, viewData); + if (hostNode) { + attachPatchData(hostNode.native, viewData); + } if (directives == null) viewData[DIRECTIVES] = directives = []; @@ -2175,7 +2178,7 @@ export function componentRefresh(adjustedElementIndex: number): void { // Only attached CheckAlways components or attached, dirty OnPush components should be checked if (viewAttached(hostView) && hostView[FLAGS] & (LViewFlags.CheckAlways | LViewFlags.Dirty)) { - detectChangesInternal(hostView, element, hostView[CONTEXT]); + detectChangesInternal(hostView, hostView[CONTEXT]); } } @@ -2435,7 +2438,7 @@ export function tick(component: T): void { function tickRootContext(rootContext: RootContext) { for (let i = 0; i < rootContext.components.length; i++) { const rootComponent = rootContext.components[i]; - renderComponentOrTemplate(getRootView(rootComponent), rootComponent); + renderComponentOrTemplate(readPatchedLViewData(rootComponent) !, rootComponent); } } @@ -2448,8 +2451,7 @@ function tickRootContext(rootContext: RootContext) { export function getRootView(component: any): LViewData { ngDevMode && assertDefined(component, 'component'); - const lElementNode = _getComponentHostLElementNode(component); - let lViewData = lElementNode.view; + let lViewData = readPatchedLViewData(component) !; while (lViewData[PARENT]) { lViewData = lViewData[PARENT] !; } @@ -2470,11 +2472,10 @@ export function getRootView(component: any): LViewData { * @param component The component which the change detection should be performed on. */ export function detectChanges(component: T): void { - const hostNode = _getComponentHostLElementNode(component); + const hostNode = getLElementFromComponent(component) !; ngDevMode && - assertDefined( - hostNode.data, 'Component host node should be attached to an LViewData instance.'); - detectChangesInternal(hostNode.data as LViewData, hostNode, component); + assertDefined(hostNode, 'Component host node should be attached to an LViewData instance.'); + detectChangesInternal(hostNode.data !, component); } /** @@ -2521,8 +2522,7 @@ export function checkNoChangesInRootView(lViewData: LViewData): void { } /** Checks the view of the component provided. Does not gate on dirty checks or execute doCheck. */ -export function detectChangesInternal( - hostView: LViewData, hostNode: LElementNode, component: T) { +export function detectChangesInternal(hostView: LViewData, component: T) { const hostTView = hostView[TVIEW]; const oldView = enterView(hostView, null); const templateFn = hostTView.template !; @@ -2569,8 +2569,8 @@ function updateViewQuery(viewQuery: ComponentQuery<{}>| null, component: T): */ export function markDirty(component: T) { ngDevMode && assertDefined(component, 'component'); - const lElementNode = _getComponentHostLElementNode(component); - markViewDirty(lElementNode.view); + const lViewData = readPatchedLViewData(component) !; + markViewDirty(lViewData); } /////////////////////////////// @@ -2881,11 +2881,9 @@ function assertDataNext(index: number, arr?: any[]) { arr.length, index, `index ${index} expected to be at the end of arr (length ${arr.length})`); } -export function _getComponentHostLElementNode( - component: T, isRootComponent?: boolean): LElementNode { +export function _getComponentHostLElementNode(component: any): LElementNode { ngDevMode && assertDefined(component, 'expecting component got null'); - const lElementNode = isRootComponent ? getLElementFromRootComponent(component) ! : - getLElementFromComponent(component) !; + const lElementNode = getLElementFromComponent(component) !; ngDevMode && assertDefined(component, 'object is not a component'); return lElementNode; } 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 13fc4f31bd..2bec21b858 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -98,9 +98,6 @@ { "name": "_CLEAN_PROMISE" }, - { - "name": "_getComponentHostLElementNode" - }, { "name": "_renderCompCount" }, @@ -137,9 +134,6 @@ { "name": "componentRefresh" }, - { - "name": "createLContext" - }, { "name": "createLNode" }, @@ -200,9 +194,6 @@ { "name": "extractPipeDef" }, - { - "name": "findViaComponent" - }, { "name": "firstTemplatePass" }, @@ -218,12 +209,6 @@ { "name": "getDirectiveDef" }, - { - "name": "getLElementFromComponent" - }, - { - "name": "getLElementFromRootComponent" - }, { "name": "getLViewChild" }, @@ -251,9 +236,6 @@ { "name": "getRenderParent" }, - { - "name": "getRootView" - }, { "name": "hostElement" }, @@ -290,6 +272,9 @@ { "name": "readPatchedData" }, + { + "name": "readPatchedLViewData" + }, { "name": "refreshChildComponents" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 875ce2c4b1..a6792814fb 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -314,9 +314,6 @@ { "name": "_devMode" }, - { - "name": "_getComponentHostLElementNode" - }, { "name": "_global" }, @@ -596,9 +593,6 @@ { "name": "getLElementFromComponent" }, - { - "name": "getLElementFromRootComponent" - }, { "name": "getLViewChild" }, @@ -671,9 +665,6 @@ { "name": "getRendererFactory" }, - { - "name": "getRootView" - }, { "name": "getStyleSanitizer" }, @@ -854,6 +845,9 @@ { "name": "readPatchedData" }, + { + "name": "readPatchedLViewData" + }, { "name": "reference" }, diff --git a/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json b/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json index 2a3bff32ac..7b2bc1abc1 100644 --- a/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json @@ -1622,9 +1622,6 @@ { "name": "getLElementFromComponent" }, - { - "name": "getLElementFromRootComponent" - }, { "name": "getLViewChild" }, @@ -1772,9 +1769,6 @@ { "name": "getRendererFactory" }, - { - "name": "getRootView" - }, { "name": "getStyleSanitizer" }, @@ -2174,6 +2168,9 @@ { "name": "readPatchedData" }, + { + "name": "readPatchedLViewData" + }, { "name": "recursivelyProcessProviders" }, diff --git a/packages/core/test/render3/integration_spec.ts b/packages/core/test/render3/integration_spec.ts index b08a163fb2..73eb7d2b73 100644 --- a/packages/core/test/render3/integration_spec.ts +++ b/packages/core/test/render3/integration_spec.ts @@ -1581,7 +1581,7 @@ describe('render3 integration test', () => { const host = fixture.hostElement; const child = host.querySelector('child-comp') as any; - expect(child[MONKEY_PATCH_KEY_NAME]).toBeFalsy(); + expect(child[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); const [kid1, kid2, kid3] = Array.from(host.querySelectorAll('child-comp > *')); expect(kid1[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); @@ -1859,7 +1859,7 @@ describe('render3 integration test', () => { expect(textNode[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); expect(section[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); - expect(projectorComp[MONKEY_PATCH_KEY_NAME]).toBeFalsy(); + expect(projectorComp[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); expect(header[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); expect(h1[MONKEY_PATCH_KEY_NAME]).toBeFalsy(); expect(p[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); @@ -1936,19 +1936,19 @@ describe('render3 integration test', () => { const hostElm = fixture.hostElement; const component = fixture.component; - const componentContext = (component as any)[MONKEY_PATCH_KEY_NAME]; - expect(Array.isArray(componentContext)).toBeFalsy(); + const componentLViewData = (component as any)[MONKEY_PATCH_KEY_NAME]; + expect(Array.isArray(componentLViewData)).toBeTruthy(); - const hostContext = (hostElm as any)[MONKEY_PATCH_KEY_NAME]; - expect(hostContext).toBe(componentContext); + const hostLViewData = (hostElm as any)[MONKEY_PATCH_KEY_NAME]; + expect(hostLViewData).toBe(componentLViewData); const context1 = getContext(hostElm) !; - expect(context1).toBe(hostContext); + expect(context1.lViewData).toBe(hostLViewData); expect(context1.native).toEqual(hostElm); const context2 = getContext(component) !; expect(context2).toBe(context1); - expect(context2).toBe(hostContext); + expect(context2.lViewData).toBe(hostLViewData); expect(context2.native).toEqual(hostElm); }); @@ -2183,7 +2183,7 @@ describe('render3 integration test', () => { const host = fixture.hostElement; const child = host.querySelector('child-comp') as any; - expect(child[MONKEY_PATCH_KEY_NAME]).toBeFalsy(); + expect(child[MONKEY_PATCH_KEY_NAME]).toBeTruthy(); const context = getContext(child) !; expect(child[MONKEY_PATCH_KEY_NAME]).toBeTruthy();