feat(ivy): add ability to inspect local refs through context discovery (#26117)
PR Close #26117
This commit is contained in:
parent
325e8010e9
commit
5f6900ecc0
@ -28,23 +28,41 @@ export const MONKEY_PATCH_KEY_NAME = '__ngContext__';
|
|||||||
* of the context.
|
* of the context.
|
||||||
*/
|
*/
|
||||||
export interface LContext {
|
export interface LContext {
|
||||||
/** The component's parent view data */
|
/**
|
||||||
|
* The component's parent view data.
|
||||||
|
*/
|
||||||
lViewData: LViewData;
|
lViewData: LViewData;
|
||||||
|
|
||||||
/** The index instance of the LNode */
|
/**
|
||||||
|
* The index instance of the LNode.
|
||||||
|
*/
|
||||||
lNodeIndex: number;
|
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;
|
native: RElement;
|
||||||
|
|
||||||
/** The instance of the Component node */
|
/**
|
||||||
|
* The instance of the Component node.
|
||||||
|
*/
|
||||||
component: {}|null|undefined;
|
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;
|
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.
|
/** 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,
|
component: undefined,
|
||||||
directiveIndices: undefined,
|
directiveIndices: undefined,
|
||||||
directives: undefined,
|
directives: undefined,
|
||||||
|
localRefs: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,6 +414,28 @@ export function discoverDirectives(lViewData: LViewData, indices: number[]): num
|
|||||||
return directives;
|
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 {
|
function getDirectiveStartIndex(tNode: TNode): number {
|
||||||
// the tNode instances store a flag value which then has a
|
// the tNode instances store a flag value which then has a
|
||||||
// pointer which tells the starting index of where all the
|
// pointer which tells the starting index of where all the
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
import {Injector} from '../di/injector';
|
import {Injector} from '../di/injector';
|
||||||
|
|
||||||
import {assertDefined} from './assert';
|
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 {LElementNode, TNode, TNodeFlags} from './interfaces/node';
|
||||||
import {CONTEXT, FLAGS, INJECTOR, LViewData, LViewFlags, PARENT, RootContext, TVIEW} from './interfaces/view';
|
import {CONTEXT, FLAGS, INJECTOR, LViewData, LViewFlags, PARENT, RootContext, TVIEW} from './interfaces/view';
|
||||||
|
|
||||||
@ -141,3 +141,16 @@ export function getRootView(componentOrView: LViewData | {}): LViewData {
|
|||||||
}
|
}
|
||||||
return 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 || {};
|
||||||
|
}
|
@ -6,9 +6,9 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {StaticInjector} from '../../src/di/injector';
|
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 {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';
|
import {ComponentFixture} from './render_util';
|
||||||
|
|
||||||
@ -266,4 +266,76 @@ describe('discovery utils', () => {
|
|||||||
expect(getInjector(fixture.hostElement)).toEqual(null);
|
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');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user