Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 48 additions & 7 deletions packages/core/src/render3/context_discovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -172,6 +190,7 @@ function createLContext(lViewData: LViewData, lNodeIndex: number, native: REleme
component: undefined,
directiveIndices: undefined,
directives: undefined,
localRefs: undefined,
};
}

Expand Down Expand Up @@ -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
Expand Down
15 changes: 14 additions & 1 deletion packages/core/src/render3/discovery_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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 || {};
}
76 changes: 74 additions & 2 deletions packages/core/test/render3/discovery_utils_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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');
});
});
});