feat(ivy): add ability to inspect local refs through context discovery (#26117)

PR Close #26117
This commit is contained in:
Pawel Kozlowski 2018-09-26 15:21:58 +02:00 committed by Alex Rickabaugh
parent 325e8010e9
commit 5f6900ecc0
3 changed files with 136 additions and 10 deletions

View File

@ -28,23 +28,41 @@ export const MONKEY_PATCH_KEY_NAME = '__ngContext__';
* of the context.
*/
export interface LContext {
/** The component's parent view data */
/**
* The component's parent view data.
*/
lViewData: LViewData;
/** The index instance of the LNode */
/**
* The index instance of the LNode.
*/
lNodeIndex: number;
/** The instance of the DOM node that is attached to the lNode */
/**
* The instance of the DOM node that is attached to the lNode.
*/
native: RElement;
/** The instance of the Component node */
/**
* The instance of the Component node.
*/
component: {}|null|undefined;
/** The list of indices for the active directives that exist on this element */
/**
* The list of indices for the active directives that exist on this element.
*/
directiveIndices: number[]|null|undefined;
/** The list of active directives that exist on this element */
directives: Array<{}>|null|undefined;
/**
* The list of active directives that exist on this element.
*/
directives: any[]|null|undefined;
/**
* The map of local references (local reference name => element or directive instance) that exist
* on this element.
*/
localRefs: {[key: string]: any}|null|undefined;
}
/** Returns the matching `LContext` data for a given DOM node, directive or component instance.
@ -172,6 +190,7 @@ function createLContext(lViewData: LViewData, lNodeIndex: number, native: REleme
component: undefined,
directiveIndices: undefined,
directives: undefined,
localRefs: undefined,
};
}
@ -395,6 +414,28 @@ export function discoverDirectives(lViewData: LViewData, indices: number[]): num
return directives;
}
/**
* Returns a map of local references (local reference name => element or directive instance) that
* exist on a given element.
*/
export function discoverLocalRefs(lViewData: LViewData, lNodeIndex: number): {[key: string]: any}|
null {
const tNode = lViewData[TVIEW].data[lNodeIndex] as TNode;
if (tNode && tNode.localNames) {
const result: {[key: string]: any} = {};
for (let i = 0; i < tNode.localNames.length; i += 2) {
const localRefName = tNode.localNames[i];
const directiveIndex = tNode.localNames[i + 1] as number;
result[localRefName] = directiveIndex === -1 ?
getLNodeFromViewData(lViewData, lNodeIndex) !.native :
lViewData[DIRECTIVES] ![directiveIndex];
}
return result;
}
return null;
}
function getDirectiveStartIndex(tNode: TNode): number {
// the tNode instances store a flag value which then has a
// pointer which tells the starting index of where all the

View File

@ -8,7 +8,7 @@
import {Injector} from '../di/injector';
import {assertDefined} from './assert';
import {LContext, discoverDirectiveIndices, discoverDirectives, getContext, isComponentInstance, readPatchedLViewData} from './context_discovery';
import {LContext, discoverDirectiveIndices, discoverDirectives, discoverLocalRefs, getContext, isComponentInstance, readPatchedLViewData} from './context_discovery';
import {LElementNode, TNode, TNodeFlags} from './interfaces/node';
import {CONTEXT, FLAGS, INJECTOR, LViewData, LViewFlags, PARENT, RootContext, TVIEW} from './interfaces/view';
@ -141,3 +141,16 @@ export function getRootView(componentOrView: LViewData | {}): LViewData {
}
return lViewData;
}
/**
* Retrieve map of local references (local reference name => element or directive instance).
*/
export function getLocalRefs(target: {}): {[key: string]: any} {
const context = loadContext(target) !;
if (context.localRefs === undefined) {
context.localRefs = discoverLocalRefs(context.lViewData, context.lNodeIndex);
}
return context.localRefs || {};
}

View File

@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import {StaticInjector} from '../../src/di/injector';
import {getComponent, getDirectives, getHostComponent, getInjector, getRootComponents} from '../../src/render3/discovery_utils';
import {getComponent, getDirectives, getHostComponent, getInjector, getLocalRefs, getRootComponents} from '../../src/render3/discovery_utils';
import {RenderFlags, defineComponent, defineDirective} from '../../src/render3/index';
import {element} from '../../src/render3/instructions';
import {element, elementEnd, elementStart, elementStyling, elementStylingApply} from '../../src/render3/instructions';
import {ComponentFixture} from './render_util';
@ -266,4 +266,76 @@ describe('discovery utils', () => {
expect(getInjector(fixture.hostElement)).toEqual(null);
});
});
describe('getLocalRefs', () => {
it('should return a map of local refs for an element', () => {
class MyDir {
static ngDirectiveDef = defineDirective({
type: MyDir,
selectors: [['', 'myDir', '']],
exportAs: 'myDir',
factory: () => new MyDir()
});
}
class Comp {
static ngComponentDef = defineComponent({
type: Comp,
selectors: [['comp']],
factory: () => new Comp(),
consts: 3,
vars: 0,
template: (rf: RenderFlags, ctx: Comp) => {
if (rf & RenderFlags.Create) {
// <div myDir #elRef #dirRef="myDir">
element(0, 'div', ['myDir'], ['elRef', '', 'dirRef', 'myDir']);
}
},
directives: [MyDir]
});
}
const fixture = new ComponentFixture(Comp);
fixture.update();
const divEl = fixture.hostElement.querySelector('div') !;
const localRefs = getLocalRefs(divEl);
expect(localRefs.elRef.tagName.toLowerCase()).toBe('div');
expect(localRefs.dirRef.constructor).toBe(MyDir);
});
it('should return a map of local refs for an element with styling context', () => {
class Comp {
static ngComponentDef = defineComponent({
type: Comp,
selectors: [['comp']],
factory: () => new Comp(),
consts: 2,
vars: 0,
template: (rf: RenderFlags, ctx: Comp) => {
if (rf & RenderFlags.Create) {
// <div #elRef class="fooClass">
elementStart(0, 'div', null, ['elRef', '']);
elementStyling(['fooClass']);
elementEnd();
}
if (rf & RenderFlags.Update) {
elementStylingApply(0);
}
}
});
}
const fixture = new ComponentFixture(Comp);
fixture.update();
const divEl = fixture.hostElement.querySelector('div') !;
const localRefs = getLocalRefs(divEl);
expect(localRefs.elRef.tagName.toLowerCase()).toBe('div');
});
});
});