From 4726ac2481faa4c86c918e0d9aa6d540af5645d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Niemel=C3=A4?= Date: Tue, 17 Sep 2019 11:19:12 -0700 Subject: [PATCH] feat(ivy): expose `window.ng.getDebugNode` helper (#32727) PR Close #32727 --- packages/core/src/render3/global_utils_api.ts | 2 +- .../src/render3/instructions/lview_debug.ts | 34 +++++++++++-------- .../core/src/render3/util/discovery_utils.ts | 33 ++++++++++++++++-- .../core/src/render3/util/global_utils.ts | 4 +-- .../test/acceptance/discover_utils_spec.ts | 29 +++++++++++++++- tools/public_api_guard/global_utils.d.ts | 2 ++ 6 files changed, 83 insertions(+), 21 deletions(-) diff --git a/packages/core/src/render3/global_utils_api.ts b/packages/core/src/render3/global_utils_api.ts index c41d44ee68..1884a8e410 100644 --- a/packages/core/src/render3/global_utils_api.ts +++ b/packages/core/src/render3/global_utils_api.ts @@ -16,4 +16,4 @@ */ export {markDirty} from './instructions/all'; -export {getComponent, getContext, getDirectives, getHostElement, getInjector, getListeners, getRootComponents, getViewComponent} from './util/discovery_utils'; +export {getComponent, getContext, getDebugNode, getDirectives, getHostElement, getInjector, getListeners, getRootComponents, getViewComponent} from './util/discovery_utils'; diff --git a/packages/core/src/render3/instructions/lview_debug.ts b/packages/core/src/render3/instructions/lview_debug.ts index 3e054571ae..03c00f44c4 100644 --- a/packages/core/src/render3/instructions/lview_debug.ts +++ b/packages/core/src/render3/instructions/lview_debug.ts @@ -358,21 +358,7 @@ export function toDebugNodes(tNode: TNode | null, lView: LView): DebugNode[]|nul const debugNodes: DebugNode[] = []; let tNodeCursor: TNode|null = tNode; while (tNodeCursor) { - const rawValue = lView[tNode.index]; - const native = unwrapRNode(rawValue); - const componentLViewDebug = toDebug(readLViewValue(rawValue)); - const styles = isStylingContext(tNode.styles) ? - new NodeStylingDebug(tNode.styles as any as TStylingContext, lView) : - null; - const classes = isStylingContext(tNode.classes) ? - new NodeStylingDebug(tNode.classes as any as TStylingContext, lView, true) : - null; - debugNodes.push({ - html: toHtml(native), - native: native as any, styles, classes, - nodes: toDebugNodes(tNode.child, lView), - component: componentLViewDebug, - }); + debugNodes.push(buildDebugNode(tNodeCursor, lView)); tNodeCursor = tNodeCursor.next; } return debugNodes; @@ -381,6 +367,24 @@ export function toDebugNodes(tNode: TNode | null, lView: LView): DebugNode[]|nul } } +export function buildDebugNode(tNode: TNode, lView: LView): DebugNode { + const rawValue = lView[tNode.index]; + const native = unwrapRNode(rawValue); + const componentLViewDebug = toDebug(readLViewValue(rawValue)); + const styles = isStylingContext(tNode.styles) ? + new NodeStylingDebug(tNode.styles as any as TStylingContext, lView) : + null; + const classes = isStylingContext(tNode.classes) ? + new NodeStylingDebug(tNode.classes as any as TStylingContext, lView, true) : + null; + return { + html: toHtml(native), + native: native as any, styles, classes, + nodes: toDebugNodes(tNode.child, lView), + component: componentLViewDebug, + }; +} + export class LContainerDebug { constructor(private readonly _raw_lContainer: LContainer) {} diff --git a/packages/core/src/render3/util/discovery_utils.ts b/packages/core/src/render3/util/discovery_utils.ts index 55ebdd1eb5..2c2155d599 100644 --- a/packages/core/src/render3/util/discovery_utils.ts +++ b/packages/core/src/render3/util/discovery_utils.ts @@ -10,14 +10,15 @@ import {Injector} from '../../di/injector'; import {assertLView} from '../assert'; import {discoverLocalRefs, getComponentAtNodeIndex, getDirectivesAtNodeIndex, getLContext} from '../context_discovery'; import {NodeInjector} from '../di'; +import {DebugNode, buildDebugNode} from '../instructions/lview_debug'; import {LContext} from '../interfaces/context'; import {DirectiveDef} from '../interfaces/definition'; import {TElementNode, TNode, TNodeProviderIndexes} from '../interfaces/node'; -import {CLEANUP, CONTEXT, FLAGS, HOST, LView, LViewFlags, TVIEW} from '../interfaces/view'; +import {CLEANUP, CONTEXT, FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, TVIEW} from '../interfaces/view'; import {stringifyForError} from './misc_utils'; import {getLViewParent, getRootContext} from './view_traversal_utils'; -import {unwrapRNode} from './view_utils'; +import {getTNode, unwrapRNode} from './view_utils'; @@ -343,3 +344,31 @@ function sortListeners(a: Listener, b: Listener) { function isDirectiveDefHack(obj: any): obj is DirectiveDef { return obj.type !== undefined && obj.template !== undefined && obj.declaredInputs !== undefined; } + +/** + * Returns the attached `DebugNode` instance for an element in the DOM. + * + * @param element DOM element which is owned by an existing component's view. + * + * @publicApi + */ +export function getDebugNode(element: Node): DebugNode|null { + let debugNode: DebugNode|null = null; + + const lContext = loadLContextFromNode(element); + const lView = lContext.lView; + let nodeIndex = -1; + for (let i = HEADER_OFFSET; i < lView.length; i++) { + if (lView[i] === element) { + nodeIndex = i - HEADER_OFFSET; + break; + } + } + + if (nodeIndex !== -1) { + const tNode = getTNode(nodeIndex, lView); + debugNode = buildDebugNode(tNode, lView); + } + + return debugNode; +} diff --git a/packages/core/src/render3/util/global_utils.ts b/packages/core/src/render3/util/global_utils.ts index e4d48167d3..cbad2b6ac5 100644 --- a/packages/core/src/render3/util/global_utils.ts +++ b/packages/core/src/render3/util/global_utils.ts @@ -7,8 +7,7 @@ */ import {assertDefined} from '../../util/assert'; import {global} from '../../util/global'; - -import {getComponent, getContext, getDirectives, getHostElement, getInjector, getListeners, getRootComponents, getViewComponent, markDirty} from '../global_utils_api'; +import {getComponent, getContext, getDebugNode, getDirectives, getHostElement, getInjector, getListeners, getRootComponents, getViewComponent, markDirty} from '../global_utils_api'; @@ -48,6 +47,7 @@ export function publishDefaultGlobalUtils() { publishGlobalUtil('getInjector', getInjector); publishGlobalUtil('getRootComponents', getRootComponents); publishGlobalUtil('getDirectives', getDirectives); + publishGlobalUtil('getDebugNode', getDebugNode); publishGlobalUtil('markDirty', markDirty); } } diff --git a/packages/core/test/acceptance/discover_utils_spec.ts b/packages/core/test/acceptance/discover_utils_spec.ts index f8b6fb2213..90af3ac198 100644 --- a/packages/core/test/acceptance/discover_utils_spec.ts +++ b/packages/core/test/acceptance/discover_utils_spec.ts @@ -11,7 +11,7 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; import {onlyInIvy} from '@angular/private/testing'; import {getHostElement, markDirty} from '../../src/render3/index'; -import {getComponent, getContext, getDirectives, getInjectionTokens, getInjector, getListeners, getLocalRefs, getRootComponents, getViewComponent, loadLContext} from '../../src/render3/util/discovery_utils'; +import {getComponent, getContext, getDebugNode, getDirectives, getInjectionTokens, getInjector, getListeners, getLocalRefs, getRootComponents, getViewComponent, loadLContext} from '../../src/render3/util/discovery_utils'; onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => { let fixture: ComponentFixture; @@ -403,4 +403,31 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () => expect(localRefs.elRef.tagName.toLowerCase()).toBe('div'); }); }); + + describe('getDebugNode()', () => { + it('should create an instance of `DebugNode` when called for a specific element', () => { + @Component({ + template: ` +
+
+
+ ` + }) + class Comp { + } + + TestBed.configureTestingModule({declarations: [Comp]}); + const fixture = TestBed.createComponent(Comp); + fixture.detectChanges(); + + const parent = fixture.nativeElement.querySelector('.parent') !; + const child = fixture.nativeElement.querySelector('.child') !; + + const parentDebug = getDebugNode(parent) !; + const childDebug = getDebugNode(child) !; + + expect(parentDebug.native).toBe(parent); + expect(childDebug.native).toBe(child); + }); + }); }); diff --git a/tools/public_api_guard/global_utils.d.ts b/tools/public_api_guard/global_utils.d.ts index 67c0a67566..a20f47d92d 100644 --- a/tools/public_api_guard/global_utils.d.ts +++ b/tools/public_api_guard/global_utils.d.ts @@ -2,6 +2,8 @@ export declare function getComponent(element: Element): T | null; export declare function getContext(element: Element): T | null; +export declare function getDebugNode(element: Node): DebugNode | null; + export declare function getDirectives(target: {}): Array<{}>; export declare function getHostElement(directive: T): Element;