fix(ivy): ensure `window.ng.getDebugNode` returns debug info for component elements (#32780)

Prior to this patch the `window.ng.getDebugNode` method would fail to
return the debug information for an element that is a host element to
a component.

PR Close #32780
This commit is contained in:
Matias Niemelä 2019-09-19 15:01:08 -07:00 committed by Andrew Kushnir
parent 353368cccd
commit 5651fa3a95
3 changed files with 47 additions and 15 deletions

View File

@ -358,7 +358,7 @@ export function toDebugNodes(tNode: TNode | null, lView: LView): DebugNode[]|nul
const debugNodes: DebugNode[] = []; const debugNodes: DebugNode[] = [];
let tNodeCursor: TNode|null = tNode; let tNodeCursor: TNode|null = tNode;
while (tNodeCursor) { while (tNodeCursor) {
debugNodes.push(buildDebugNode(tNodeCursor, lView)); debugNodes.push(buildDebugNode(tNodeCursor, lView, tNodeCursor.index));
tNodeCursor = tNodeCursor.next; tNodeCursor = tNodeCursor.next;
} }
return debugNodes; return debugNodes;
@ -367,8 +367,8 @@ export function toDebugNodes(tNode: TNode | null, lView: LView): DebugNode[]|nul
} }
} }
export function buildDebugNode(tNode: TNode, lView: LView): DebugNode { export function buildDebugNode(tNode: TNode, lView: LView, nodeIndex: number): DebugNode {
const rawValue = lView[tNode.index]; const rawValue = lView[nodeIndex];
const native = unwrapRNode(rawValue); const native = unwrapRNode(rawValue);
const componentLViewDebug = toDebug(readLViewValue(rawValue)); const componentLViewDebug = toDebug(readLViewValue(rawValue));
const styles = isStylingContext(tNode.styles) ? const styles = isStylingContext(tNode.styles) ?

View File

@ -14,7 +14,8 @@ import {DebugNode, buildDebugNode} from '../instructions/lview_debug';
import {LContext} from '../interfaces/context'; import {LContext} from '../interfaces/context';
import {DirectiveDef} from '../interfaces/definition'; import {DirectiveDef} from '../interfaces/definition';
import {TElementNode, TNode, TNodeProviderIndexes} from '../interfaces/node'; import {TElementNode, TNode, TNodeProviderIndexes} from '../interfaces/node';
import {CLEANUP, CONTEXT, FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, TVIEW} from '../interfaces/view'; import {isLView} from '../interfaces/type_checks';
import {CLEANUP, CONTEXT, FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, TVIEW, T_HOST} from '../interfaces/view';
import {stringifyForError} from './misc_utils'; import {stringifyForError} from './misc_utils';
import {getLViewParent, getRootContext} from './view_traversal_utils'; import {getLViewParent, getRootContext} from './view_traversal_utils';
@ -357,17 +358,14 @@ export function getDebugNode(element: Node): DebugNode|null {
const lContext = loadLContextFromNode(element); const lContext = loadLContextFromNode(element);
const lView = lContext.lView; const lView = lContext.lView;
let nodeIndex = -1; const nodeIndex = lContext.nodeIndex;
for (let i = HEADER_OFFSET; i < lView.length; i++) {
if (lView[i] === element) {
nodeIndex = i - HEADER_OFFSET;
break;
}
}
if (nodeIndex !== -1) { if (nodeIndex !== -1) {
const tNode = getTNode(nodeIndex, lView); const valueInLView = lView[nodeIndex];
debugNode = buildDebugNode(tNode, lView); // this means that value in the lView is a component with its own
// data. In this situation the TNode is not accessed at the same spot.
const tNode = isLView(valueInLView) ? (valueInLView[T_HOST] as TNode) :
getTNode(nodeIndex - HEADER_OFFSET, lView);
debugNode = buildDebugNode(tNode, lView, nodeIndex);
} }
return debugNode; return debugNode;

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {CommonModule} from '@angular/common'; import {CommonModule} from '@angular/common';
import {Component, Directive, InjectionToken, ViewChild} from '@angular/core'; import {Component, Directive, HostBinding, InjectionToken, ViewChild} from '@angular/core';
import {ComponentFixture, TestBed} from '@angular/core/testing'; import {ComponentFixture, TestBed} from '@angular/core/testing';
import {onlyInIvy} from '@angular/private/testing'; import {onlyInIvy} from '@angular/private/testing';
@ -429,5 +429,39 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
expect(parentDebug.native).toBe(parent); expect(parentDebug.native).toBe(parent);
expect(childDebug.native).toBe(child); expect(childDebug.native).toBe(child);
}); });
it('should be able to pull debug information for a component host element', () => {
@Component({
selector: 'child-comp',
template: `
child comp
`
})
class ChildComp {
@HostBinding('style') public styles = {width: '200px', height: '400px'};
}
@Component({
template: `
<child-comp></child-comp>
`
})
class Comp {
}
TestBed.configureTestingModule({declarations: [Comp, ChildComp]});
const fixture = TestBed.createComponent(Comp);
fixture.detectChanges();
const child = fixture.nativeElement.querySelector('child-comp') !;
const childDebug = getDebugNode(child) !;
expect(childDebug.native).toBe(child);
expect(childDebug.styles).toBeTruthy();
const styles = childDebug.styles !.values;
expect(styles['width']).toEqual('200px');
expect(styles['height']).toEqual('400px');
});
}); });
}); });