fix(ivy): Implement remaining methods for DebugNode (#27387)
PR Close #27387
This commit is contained in:
parent
f0b0d64453
commit
b2d6f43b49
|
@ -154,7 +154,7 @@ export {
|
|||
} from './sanitization/bypass';
|
||||
|
||||
export {
|
||||
getContext as ɵgetContext
|
||||
getLContext as ɵgetLContext
|
||||
} from './render3/context_discovery';
|
||||
|
||||
export {
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
*/
|
||||
|
||||
import {Injector} from '../di';
|
||||
import {DirectiveDef} from '../render3';
|
||||
import {assertDomNode} from '../render3/assert';
|
||||
import {getComponent, getInjector, getLocalRefs, loadContext} from '../render3/discovery_utils';
|
||||
import {TNode, TNodeFlags} from '../render3/interfaces/node';
|
||||
import {getComponent, getContext, getInjectionTokens, getInjector, getListeners, getLocalRefs, isBrowserEvents, loadLContext, loadLContextFromNode} from '../render3/discovery_utils';
|
||||
import {TNode} from '../render3/interfaces/node';
|
||||
import {StylingIndex} from '../render3/interfaces/styling';
|
||||
import {TVIEW} from '../render3/interfaces/view';
|
||||
import {getProp, getValue, isClassBased} from '../render3/styling/class_and_style_bindings';
|
||||
import {getStylingContext} from '../render3/styling/util';
|
||||
import {DebugContext} from '../view/index';
|
||||
|
||||
export class EventListener {
|
||||
|
@ -204,7 +206,7 @@ class DebugNode__POST_R3__ implements DebugNode {
|
|||
constructor(nativeNode: Node) { this.nativeNode = nativeNode; }
|
||||
|
||||
get parent(): DebugElement|null {
|
||||
const parent = this.nativeNode.parentNode as HTMLElement;
|
||||
const parent = this.nativeNode.parentNode as Element;
|
||||
return parent ? new DebugElement__POST_R3__(parent) : null;
|
||||
}
|
||||
|
||||
|
@ -212,46 +214,17 @@ class DebugNode__POST_R3__ implements DebugNode {
|
|||
|
||||
get componentInstance(): any {
|
||||
const nativeElement = this.nativeNode;
|
||||
return nativeElement && getComponent(nativeElement as HTMLElement);
|
||||
}
|
||||
get context(): any {
|
||||
// https://angular-team.atlassian.net/browse/FW-719
|
||||
throw notImplemented();
|
||||
return nativeElement && getComponent(nativeElement as Element);
|
||||
}
|
||||
get context(): any { return getContext(this.nativeNode as Element); }
|
||||
|
||||
get listeners(): EventListener[] {
|
||||
// TODO: add real implementation;
|
||||
// https://angular-team.atlassian.net/browse/FW-719
|
||||
return [];
|
||||
return getListeners(this.nativeNode as Element).filter(isBrowserEvents);
|
||||
}
|
||||
|
||||
get references(): {[key: string]: any;} { return getLocalRefs(this.nativeNode); }
|
||||
|
||||
get providerTokens(): any[] {
|
||||
// TODO move to discoverable utils
|
||||
const context = loadContext(this.nativeNode as HTMLElement, false) !;
|
||||
if (!context) return [];
|
||||
const lView = context.lView;
|
||||
const tView = lView[TVIEW];
|
||||
const tNode = tView.data[context.nodeIndex] as TNode;
|
||||
const providerTokens: any[] = [];
|
||||
const nodeFlags = tNode.flags;
|
||||
const startIndex = nodeFlags >> TNodeFlags.DirectiveStartingIndexShift;
|
||||
const directiveCount = nodeFlags & TNodeFlags.DirectiveCountMask;
|
||||
const endIndex = startIndex + directiveCount;
|
||||
for (let i = startIndex; i < endIndex; i++) {
|
||||
let value = tView.data[i];
|
||||
if (isDirectiveDefHack(value)) {
|
||||
// The fact that we sometimes store Type and sometimes DirectiveDef in this location is a
|
||||
// design flaw. We should always store same type so that we can be monomorphic. The issue
|
||||
// is that for Components/Directives we store the def instead the type. The correct behavior
|
||||
// is that we should always be storing injectable type in this location.
|
||||
value = value.type;
|
||||
}
|
||||
providerTokens.push(value);
|
||||
}
|
||||
return providerTokens;
|
||||
}
|
||||
get providerTokens(): any[] { return getInjectionTokens(this.nativeNode as Element); }
|
||||
}
|
||||
|
||||
class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugElement {
|
||||
|
@ -264,10 +237,10 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
|
|||
return this.nativeNode.nodeType == Node.ELEMENT_NODE ? this.nativeNode as Element : null;
|
||||
}
|
||||
|
||||
get name(): string { return (this.nativeElement as HTMLElement).nodeName; }
|
||||
get name(): string { return this.nativeElement !.nodeName; }
|
||||
|
||||
get properties(): {[key: string]: any;} {
|
||||
const context = loadContext(this.nativeNode) !;
|
||||
const context = loadLContext(this.nativeNode) !;
|
||||
const lView = context.lView;
|
||||
const tView = lView[TVIEW];
|
||||
const tNode = tView.data[context.nodeIndex] as TNode;
|
||||
|
@ -278,18 +251,77 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
|
|||
}
|
||||
|
||||
get attributes(): {[key: string]: string | null;} {
|
||||
// https://angular-team.atlassian.net/browse/FW-719
|
||||
throw notImplemented();
|
||||
const attributes: {[key: string]: string | null;} = {};
|
||||
const element = this.nativeElement;
|
||||
if (element) {
|
||||
const eAttrs = element.attributes;
|
||||
for (let i = 0; i < eAttrs.length; i++) {
|
||||
const attr = eAttrs[i];
|
||||
attributes[attr.name] = attr.value;
|
||||
}
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
|
||||
get classes(): {[key: string]: boolean;} {
|
||||
// https://angular-team.atlassian.net/browse/FW-719
|
||||
throw notImplemented();
|
||||
const classes: {[key: string]: boolean;} = {};
|
||||
const element = this.nativeElement;
|
||||
if (element) {
|
||||
const lContext = loadLContextFromNode(element);
|
||||
const lNode = lContext.lView[lContext.nodeIndex];
|
||||
const stylingContext = getStylingContext(lContext.nodeIndex, lContext.lView);
|
||||
if (stylingContext) {
|
||||
for (let i = StylingIndex.SingleStylesStartPosition; i < lNode.length;
|
||||
i += StylingIndex.Size) {
|
||||
if (isClassBased(lNode, i)) {
|
||||
const className = getProp(lNode, i);
|
||||
const value = getValue(lNode, i);
|
||||
if (typeof value == 'boolean') {
|
||||
// we want to ignore `null` since those don't overwrite the values.
|
||||
classes[className] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Fallback, just read DOM.
|
||||
const eClasses = element.classList;
|
||||
for (let i = 0; i < eClasses.length; i++) {
|
||||
classes[eClasses[i]] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
get styles(): {[key: string]: string | null;} {
|
||||
// https://angular-team.atlassian.net/browse/FW-719
|
||||
throw notImplemented();
|
||||
const styles: {[key: string]: string | null;} = {};
|
||||
const element = this.nativeElement;
|
||||
if (element) {
|
||||
const lContext = loadLContextFromNode(element);
|
||||
const lNode = lContext.lView[lContext.nodeIndex];
|
||||
const stylingContext = getStylingContext(lContext.nodeIndex, lContext.lView);
|
||||
if (stylingContext) {
|
||||
for (let i = StylingIndex.SingleStylesStartPosition; i < lNode.length;
|
||||
i += StylingIndex.Size) {
|
||||
if (!isClassBased(lNode, i)) {
|
||||
const styleName = getProp(lNode, i);
|
||||
const value = getValue(lNode, i) as string | null;
|
||||
if (value !== null) {
|
||||
// we want to ignore `null` since those don't overwrite the values.
|
||||
styles[styleName] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Fallback, just read DOM.
|
||||
const eStyles = (element as HTMLElement).style;
|
||||
for (let i = 0; i < eStyles.length; i++) {
|
||||
const name = eStyles.item(i);
|
||||
styles[name] = eStyles.getPropertyValue(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return styles;
|
||||
}
|
||||
|
||||
get childNodes(): DebugNode[] {
|
||||
|
@ -332,24 +364,14 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
|
|||
}
|
||||
|
||||
triggerEventHandler(eventName: string, eventObj: any): void {
|
||||
// This is a hack implementation. The correct implementation would bypass the DOM and `TNode`
|
||||
// information to invoke the listeners directly.
|
||||
// https://angular-team.atlassian.net/browse/FW-719
|
||||
const event = document.createEvent('MouseEvent');
|
||||
event.initEvent(eventName, true, true);
|
||||
(this.nativeElement as HTMLElement).dispatchEvent(event);
|
||||
this.listeners.forEach((listener) => {
|
||||
if (listener.name === eventName) {
|
||||
listener.callback(eventObj);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function should not exist because it is megamorphic and only mostly correct.
|
||||
*
|
||||
* See call site for more info.
|
||||
*/
|
||||
function isDirectiveDefHack(obj: any): obj is DirectiveDef<any> {
|
||||
return obj.type !== undefined && obj.template !== undefined && obj.declaredInputs !== undefined;
|
||||
}
|
||||
|
||||
function _queryNodeChildrenR3(
|
||||
parentNode: DebugNode, predicate: Predicate<DebugNode>, matches: DebugNode[],
|
||||
elementsOnly: boolean) {
|
||||
|
|
|
@ -17,12 +17,12 @@ import {getComponentDef} from './definition';
|
|||
import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di';
|
||||
import {publishDefaultGlobalUtils} from './global_utils';
|
||||
import {queueInitHooks, queueLifecycleHooks} from './hooks';
|
||||
import {CLEAN_PROMISE, createLView, createNodeAtIndex, createTView, getOrCreateTView, initNodeFlags, instantiateRootComponent, locateHostElement, queueComponentIndexForCheck, refreshDescendantViews} from './instructions';
|
||||
import {CLEAN_PROMISE, createLView, createNodeAtIndex, createTNode, createTView, getOrCreateTView, initNodeFlags, instantiateRootComponent, locateHostElement, queueComponentIndexForCheck, refreshDescendantViews} from './instructions';
|
||||
import {ComponentDef, ComponentType, RenderFlags} from './interfaces/definition';
|
||||
import {TElementNode, TNodeFlags, TNodeType} from './interfaces/node';
|
||||
import {TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
|
||||
import {PlayerHandler} from './interfaces/player';
|
||||
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
|
||||
import {CONTEXT, HEADER_OFFSET, HOST, HOST_NODE, INJECTOR, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
|
||||
import {CONTEXT, HEADER_OFFSET, HOST, HOST_NODE, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
|
||||
import {enterView, getPreviousOrParentTNode, leaveView, resetComponentState, setCurrentDirectiveDef} from './state';
|
||||
import {defaultScheduler, getRootView, readPatchedLView, stringify} from './util';
|
||||
|
||||
|
@ -235,7 +235,9 @@ export function LifecycleHooksFeature(component: any, def: ComponentDef<any>): v
|
|||
const dirIndex = rootTView.data.length - 1;
|
||||
|
||||
queueInitHooks(dirIndex, def.onInit, def.doCheck, rootTView);
|
||||
queueLifecycleHooks(dirIndex << TNodeFlags.DirectiveStartingIndexShift | 1, rootTView);
|
||||
// TODO(misko): replace `as TNode` with createTNode call. (needs refactoring to lose dep on
|
||||
// LNode).
|
||||
queueLifecycleHooks(rootTView, { directiveStart: dirIndex, directiveEnd: dirIndex + 1 } as TNode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -36,7 +36,7 @@ import {getComponentViewByIndex, getNativeByTNode, readElementValue, readPatched
|
|||
*
|
||||
* @param target Component, Directive or DOM Node.
|
||||
*/
|
||||
export function getContext(target: any): LContext|null {
|
||||
export function getLContext(target: any): LContext|null {
|
||||
let mpValue = readPatchedData(target);
|
||||
if (mpValue) {
|
||||
// only when it's an array is it considered an LView instance
|
||||
|
@ -250,8 +250,8 @@ function findViaDirective(lView: LView, directiveInstance: {}): number {
|
|||
// list of directives for the instance.
|
||||
let tNode = lView[TVIEW].firstChild;
|
||||
while (tNode) {
|
||||
const directiveIndexStart = getDirectiveStartIndex(tNode);
|
||||
const directiveIndexEnd = getDirectiveEndIndex(tNode, directiveIndexStart);
|
||||
const directiveIndexStart = tNode.directiveStart;
|
||||
const directiveIndexEnd = tNode.directiveEnd;
|
||||
for (let i = directiveIndexStart; i < directiveIndexEnd; i++) {
|
||||
if (lView[i] === directiveInstance) {
|
||||
return tNode.index;
|
||||
|
@ -273,16 +273,16 @@ function findViaDirective(lView: LView, directiveInstance: {}): number {
|
|||
export function getDirectivesAtNodeIndex(
|
||||
nodeIndex: number, lView: LView, includeComponents: boolean): any[]|null {
|
||||
const tNode = lView[TVIEW].data[nodeIndex] as TNode;
|
||||
let directiveStartIndex = getDirectiveStartIndex(tNode);
|
||||
let directiveStartIndex = tNode.directiveStart;
|
||||
if (directiveStartIndex == 0) return EMPTY_ARRAY;
|
||||
const directiveEndIndex = getDirectiveEndIndex(tNode, directiveStartIndex);
|
||||
const directiveEndIndex = tNode.directiveEnd;
|
||||
if (!includeComponents && tNode.flags & TNodeFlags.isComponent) directiveStartIndex++;
|
||||
return lView.slice(directiveStartIndex, directiveEndIndex);
|
||||
}
|
||||
|
||||
export function getComponentAtNodeIndex(nodeIndex: number, lView: LView): {}|null {
|
||||
const tNode = lView[TVIEW].data[nodeIndex] as TNode;
|
||||
let directiveStartIndex = getDirectiveStartIndex(tNode);
|
||||
let directiveStartIndex = tNode.directiveStart;
|
||||
return tNode.flags & TNodeFlags.isComponent ? lView[directiveStartIndex] : null;
|
||||
}
|
||||
|
||||
|
@ -305,18 +305,3 @@ export function discoverLocalRefs(lView: LView, nodeIndex: number): {[key: strin
|
|||
|
||||
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
|
||||
// active directives are in the master directive array
|
||||
return tNode.flags >> TNodeFlags.DirectiveStartingIndexShift;
|
||||
}
|
||||
|
||||
function getDirectiveEndIndex(tNode: TNode, startIndex: number): number {
|
||||
// The end value is also a part of the same flag
|
||||
// (see `TNodeFlags` to see how the flag bit shifting
|
||||
// values are used).
|
||||
const count = tNode.flags & TNodeFlags.DirectiveCountMask;
|
||||
return count ? (startIndex + count) : -1;
|
||||
}
|
||||
|
|
|
@ -393,30 +393,32 @@ export function getOrCreateInjectable<T>(
|
|||
const NOT_FOUND = {};
|
||||
|
||||
function searchTokensOnInjector<T>(
|
||||
injectorIndex: number, injectorView: LView, token: Type<T>| InjectionToken<T>,
|
||||
injectorIndex: number, lView: LView, token: Type<T>| InjectionToken<T>,
|
||||
previousTView: TView | null) {
|
||||
const currentTView = injectorView[TVIEW];
|
||||
const currentTView = lView[TVIEW];
|
||||
const tNode = currentTView.data[injectorIndex + TNODE] as TNode;
|
||||
// First, we step through providers
|
||||
let canAccessViewProviders = false;
|
||||
// We need to determine if view providers can be accessed by the starting element.
|
||||
// It happens in 2 cases:
|
||||
// 1) On the initial element injector , if we are instantiating a token which can see the
|
||||
// viewProviders of the component of that element. Such token are:
|
||||
// - the component itself (but not other directives)
|
||||
// - viewProviders tokens of the component (but not providers tokens)
|
||||
// 2) Upper in the element injector tree, if the starting element is actually in the view of
|
||||
// the current element. To determine this, we track the transition of view during the climb,
|
||||
// and check the host node of the current view to identify component views.
|
||||
if (previousTView == null && isComponent(tNode) && includeViewProviders ||
|
||||
previousTView != null && previousTView != currentTView &&
|
||||
(currentTView.node == null || currentTView.node.type === TNodeType.Element)) {
|
||||
canAccessViewProviders = true;
|
||||
}
|
||||
const injectableIdx =
|
||||
locateDirectiveOrProvider(tNode, injectorView, token, canAccessViewProviders);
|
||||
// First, we need to determine if view providers can be accessed by the starting element.
|
||||
// There are two possibities
|
||||
const canAccessViewProviders = previousTView == null ?
|
||||
// 1) This is the first invocation `previousTView == null` which means that we are at the
|
||||
// `TNode` of where injector is starting to look. In such a case the only time we are allowed
|
||||
// to look into the ViewProviders is if:
|
||||
// - we are on a component
|
||||
// - AND the injector set `includeViewProviders` to true (implying that the token can see
|
||||
// ViewProviders because it is the Component or a Service which itself was declared in
|
||||
// ViewProviders)
|
||||
(isComponent(tNode) && includeViewProviders) :
|
||||
// 2) `previousTView != null` which means that we are now walking across the parent nodes.
|
||||
// In such a case we are only allowed to look into the ViewProviders if:
|
||||
// - We just crossed from child View to Parent View `previousTView != currentTView`
|
||||
// - AND the parent TNode is an Element.
|
||||
// This means that we just came from the Component's View and therefore are allowed to see
|
||||
// into the ViewProviders.
|
||||
(previousTView != currentTView && (tNode.type === TNodeType.Element));
|
||||
|
||||
const injectableIdx = locateDirectiveOrProvider(tNode, lView, token, canAccessViewProviders);
|
||||
if (injectableIdx !== null) {
|
||||
return getNodeInjectable(currentTView.data, injectorView, injectableIdx, tNode as TElementNode);
|
||||
return getNodeInjectable(currentTView.data, lView, injectableIdx, tNode as TElementNode);
|
||||
} else {
|
||||
return NOT_FOUND;
|
||||
}
|
||||
|
@ -439,17 +441,17 @@ export function locateDirectiveOrProvider<T>(
|
|||
const nodeProviderIndexes = tNode.providerIndexes;
|
||||
const tInjectables = tView.data;
|
||||
|
||||
const startInjectables = nodeProviderIndexes & TNodeProviderIndexes.ProvidersStartIndexMask;
|
||||
const startDirectives = nodeFlags >> TNodeFlags.DirectiveStartingIndexShift;
|
||||
const injectablesStart = nodeProviderIndexes & TNodeProviderIndexes.ProvidersStartIndexMask;
|
||||
const directivesStart = tNode.directiveStart;
|
||||
const directiveEnd = tNode.directiveEnd;
|
||||
const cptViewProvidersCount =
|
||||
nodeProviderIndexes >> TNodeProviderIndexes.CptViewProvidersCountShift;
|
||||
const startingIndex =
|
||||
canAccessViewProviders ? startInjectables : startInjectables + cptViewProvidersCount;
|
||||
const directiveCount = nodeFlags & TNodeFlags.DirectiveCountMask;
|
||||
for (let i = startingIndex; i < startDirectives + directiveCount; i++) {
|
||||
canAccessViewProviders ? injectablesStart : injectablesStart + cptViewProvidersCount;
|
||||
for (let i = startingIndex; i < directiveEnd; i++) {
|
||||
const providerTokenOrDef = tInjectables[i] as InjectionToken<any>| Type<any>| DirectiveDef<any>;
|
||||
if (i < startDirectives && token === providerTokenOrDef ||
|
||||
i >= startDirectives && (providerTokenOrDef as DirectiveDef<any>).type === token) {
|
||||
if (i < directivesStart && token === providerTokenOrDef ||
|
||||
i >= directivesStart && (providerTokenOrDef as DirectiveDef<any>).type === token) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,12 +75,11 @@ function resolveProvider(
|
|||
let token: any = isTypeProvider(provider) ? provider : resolveForwardRef(provider.provide);
|
||||
let providerFactory: () => any = providerToFactory(provider);
|
||||
|
||||
const previousOrParentTNode = getPreviousOrParentTNode();
|
||||
const beginIndex =
|
||||
previousOrParentTNode.providerIndexes & TNodeProviderIndexes.ProvidersStartIndexMask;
|
||||
const endIndex = previousOrParentTNode.flags >> TNodeFlags.DirectiveStartingIndexShift;
|
||||
const tNode = getPreviousOrParentTNode();
|
||||
const beginIndex = tNode.providerIndexes & TNodeProviderIndexes.ProvidersStartIndexMask;
|
||||
const endIndex = tNode.directiveStart;
|
||||
const cptViewProvidersCount =
|
||||
previousOrParentTNode.providerIndexes >> TNodeProviderIndexes.CptViewProvidersCountShift;
|
||||
tNode.providerIndexes >> TNodeProviderIndexes.CptViewProvidersCountShift;
|
||||
|
||||
if (isTypeProvider(provider) || !provider.multi) {
|
||||
// Single provider case: the factory is created and pushed immediately
|
||||
|
@ -91,14 +90,13 @@ function resolveProvider(
|
|||
if (existingFactoryIndex == -1) {
|
||||
diPublicInInjector(
|
||||
getOrCreateNodeInjectorForNode(
|
||||
previousOrParentTNode as TElementNode | TContainerNode | TElementContainerNode,
|
||||
lView),
|
||||
tNode as TElementNode | TContainerNode | TElementContainerNode, lView),
|
||||
lView, token);
|
||||
tInjectables.push(token);
|
||||
previousOrParentTNode.flags += 1 << TNodeFlags.DirectiveStartingIndexShift;
|
||||
tNode.directiveStart++;
|
||||
tNode.directiveEnd++;
|
||||
if (isViewProvider) {
|
||||
previousOrParentTNode.providerIndexes +=
|
||||
TNodeProviderIndexes.CptViewProvidersCountShifter;
|
||||
tNode.providerIndexes += TNodeProviderIndexes.CptViewProvidersCountShifter;
|
||||
}
|
||||
lInjectablesBlueprint.push(factory);
|
||||
lView.push(factory);
|
||||
|
@ -142,8 +140,7 @@ function resolveProvider(
|
|||
// Cases 1.a and 2.a
|
||||
diPublicInInjector(
|
||||
getOrCreateNodeInjectorForNode(
|
||||
previousOrParentTNode as TElementNode | TContainerNode | TElementContainerNode,
|
||||
lView),
|
||||
tNode as TElementNode | TContainerNode | TElementContainerNode, lView),
|
||||
lView, token);
|
||||
const factory = multiFactory(
|
||||
isViewProvider ? multiViewProvidersFactoryResolver : multiProvidersFactoryResolver,
|
||||
|
@ -152,10 +149,10 @@ function resolveProvider(
|
|||
lInjectablesBlueprint[existingViewProvidersFactoryIndex].providerFactory = factory;
|
||||
}
|
||||
tInjectables.push(token);
|
||||
previousOrParentTNode.flags += 1 << TNodeFlags.DirectiveStartingIndexShift;
|
||||
tNode.directiveStart++;
|
||||
tNode.directiveEnd++;
|
||||
if (isViewProvider) {
|
||||
previousOrParentTNode.providerIndexes +=
|
||||
TNodeProviderIndexes.CptViewProvidersCountShifter;
|
||||
tNode.providerIndexes += TNodeProviderIndexes.CptViewProvidersCountShifter;
|
||||
}
|
||||
lInjectablesBlueprint.push(factory);
|
||||
lView.push(factory);
|
||||
|
|
|
@ -8,10 +8,12 @@
|
|||
import {Injector} from '../di/injector';
|
||||
|
||||
import {assertDefined} from './assert';
|
||||
import {discoverLocalRefs, getComponentAtNodeIndex, getContext, getDirectivesAtNodeIndex} from './context_discovery';
|
||||
import {discoverLocalRefs, getComponentAtNodeIndex, getDirectivesAtNodeIndex, getLContext} from './context_discovery';
|
||||
import {LContext} from './interfaces/context';
|
||||
import {TElementNode} from './interfaces/node';
|
||||
import {CONTEXT, FLAGS, HOST, LView, LViewFlags, PARENT, RootContext, TVIEW} from './interfaces/view';
|
||||
import {DirectiveDef} from './interfaces/definition';
|
||||
import {INJECTOR_BLOOM_PARENT_SIZE} from './interfaces/injector';
|
||||
import {TElementNode, TNode, TNodeProviderIndexes} from './interfaces/node';
|
||||
import {CLEANUP, CONTEXT, FLAGS, HOST, LView, LViewFlags, PARENT, RootContext, TVIEW} from './interfaces/view';
|
||||
import {readPatchedLView, stringify} from './util';
|
||||
import {NodeInjector} from './view_engine_compatibility';
|
||||
|
||||
|
@ -20,7 +22,7 @@ import {NodeInjector} from './view_engine_compatibility';
|
|||
* Returns the component instance associated with a given DOM host element.
|
||||
* Elements which don't represent components return `null`.
|
||||
*
|
||||
* @param element Host DOM element from which the component should be retrieved for.
|
||||
* @param element Host DOM element from which the component should be retrieved.
|
||||
*
|
||||
* ```
|
||||
* <my-app>
|
||||
|
@ -37,9 +39,7 @@ import {NodeInjector} from './view_engine_compatibility';
|
|||
* @publicApi
|
||||
*/
|
||||
export function getComponent<T = {}>(element: Element): T|null {
|
||||
if (!(element instanceof Node)) throw new Error('Expecting instance of DOM Node');
|
||||
|
||||
const context = loadContext(element) !;
|
||||
const context = loadLContextFromNode(element);
|
||||
|
||||
if (context.component === undefined) {
|
||||
context.component = getComponentAtNodeIndex(context.nodeIndex, context.lView);
|
||||
|
@ -48,6 +48,31 @@ export function getComponent<T = {}>(element: Element): T|null {
|
|||
return context.component as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the component instance associated with a given DOM host element.
|
||||
* Elements which don't represent components return `null`.
|
||||
*
|
||||
* @param element Host DOM element from which the component should be retrieved.
|
||||
*
|
||||
* ```
|
||||
* <my-app>
|
||||
* #VIEW
|
||||
* <div>
|
||||
* <child-comp></child-comp>
|
||||
* </div>
|
||||
* </mp-app>
|
||||
*
|
||||
* expect(getComponent(<child-comp>) instanceof ChildComponent).toBeTruthy();
|
||||
* expect(getComponent(<my-app>) instanceof MyApp).toBeTruthy();
|
||||
* ```
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export function getContext<T = {}>(element: Element): T|null {
|
||||
const context = loadLContextFromNode(element) !;
|
||||
return context.lView[CONTEXT] as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the component instance associated with view which owns the DOM element (`null`
|
||||
* otherwise).
|
||||
|
@ -69,7 +94,7 @@ export function getComponent<T = {}>(element: Element): T|null {
|
|||
* @publicApi
|
||||
*/
|
||||
export function getViewComponent<T = {}>(element: Element | {}): T|null {
|
||||
const context = loadContext(element) !;
|
||||
const context = loadLContext(element) !;
|
||||
let lView: LView = context.lView;
|
||||
while (lView[PARENT] && lView[HOST] === null) {
|
||||
// As long as lView[HOST] is null we know we are part of sub-template such as `*ngIf`
|
||||
|
@ -87,8 +112,8 @@ export function getViewComponent<T = {}>(element: Element | {}): T|null {
|
|||
*
|
||||
*/
|
||||
export function getRootContext(target: LView | {}): RootContext {
|
||||
const lView = Array.isArray(target) ? target : loadContext(target) !.lView;
|
||||
const rootLView = getRootView(lView);
|
||||
const lViewData = Array.isArray(target) ? target : loadLContext(target) !.lView;
|
||||
const rootLView = getRootView(lViewData);
|
||||
return rootLView[CONTEXT] as RootContext;
|
||||
}
|
||||
|
||||
|
@ -113,12 +138,40 @@ export function getRootComponents(target: {}): any[] {
|
|||
* @publicApi
|
||||
*/
|
||||
export function getInjector(target: {}): Injector {
|
||||
const context = loadContext(target);
|
||||
const context = loadLContext(target);
|
||||
const tNode = context.lView[TVIEW].data[context.nodeIndex] as TElementNode;
|
||||
|
||||
return new NodeInjector(tNode, context.lView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a set of injection tokens at a given DOM node.
|
||||
*
|
||||
* @param element Element for which the injection tokens should be retrieved.
|
||||
* @publicApi
|
||||
*/
|
||||
export function getInjectionTokens(element: Element): any[] {
|
||||
const context = loadLContext(element, false);
|
||||
if (!context) return [];
|
||||
const lView = context.lView;
|
||||
const tView = lView[TVIEW];
|
||||
const tNode = tView.data[context.nodeIndex] as TNode;
|
||||
const providerTokens: any[] = [];
|
||||
const startIndex = tNode.providerIndexes & TNodeProviderIndexes.ProvidersStartIndexMask;
|
||||
const endIndex = tNode.directiveEnd;
|
||||
for (let i = startIndex; i < endIndex; i++) {
|
||||
let value = tView.data[i];
|
||||
if (isDirectiveDefHack(value)) {
|
||||
// The fact that we sometimes store Type and sometimes DirectiveDef in this location is a
|
||||
// design flaw. We should always store same type so that we can be monomorphic. The issue
|
||||
// is that for Components/Directives we store the def instead the type. The correct behavior
|
||||
// is that we should always be storing injectable type in this location.
|
||||
value = value.type;
|
||||
}
|
||||
providerTokens.push(value);
|
||||
}
|
||||
return providerTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves directives associated with a given DOM host element.
|
||||
*
|
||||
|
@ -127,7 +180,7 @@ export function getInjector(target: {}): Injector {
|
|||
* @publicApi
|
||||
*/
|
||||
export function getDirectives(target: {}): Array<{}> {
|
||||
const context = loadContext(target) !;
|
||||
const context = loadLContext(target) !;
|
||||
|
||||
if (context.directives === undefined) {
|
||||
context.directives = getDirectivesAtNodeIndex(context.nodeIndex, context.lView, false);
|
||||
|
@ -141,10 +194,10 @@ export function getDirectives(target: {}): Array<{}> {
|
|||
* Throws if a given target doesn't have associated LContext.
|
||||
*
|
||||
*/
|
||||
export function loadContext(target: {}): LContext;
|
||||
export function loadContext(target: {}, throwOnNotFound: false): LContext|null;
|
||||
export function loadContext(target: {}, throwOnNotFound: boolean = true): LContext|null {
|
||||
const context = getContext(target);
|
||||
export function loadLContext(target: {}): LContext;
|
||||
export function loadLContext(target: {}, throwOnNotFound: false): LContext|null;
|
||||
export function loadLContext(target: {}, throwOnNotFound: boolean = true): LContext|null {
|
||||
const context = getLContext(target);
|
||||
if (!context && throwOnNotFound) {
|
||||
throw new Error(
|
||||
ngDevMode ? `Unable to find context associated with ${stringify(target)}` :
|
||||
|
@ -185,7 +238,7 @@ export function getRootView(componentOrView: LView | {}): LView {
|
|||
* @publicApi
|
||||
*/
|
||||
export function getLocalRefs(target: {}): {[key: string]: any} {
|
||||
const context = loadContext(target) !;
|
||||
const context = loadLContext(target) !;
|
||||
|
||||
if (context.localRefs === undefined) {
|
||||
context.localRefs = discoverLocalRefs(context.lView, context.nodeIndex);
|
||||
|
@ -205,7 +258,7 @@ export function getLocalRefs(target: {}): {[key: string]: any} {
|
|||
* @publicApi
|
||||
*/
|
||||
export function getHostElement<T>(directive: T): Element {
|
||||
return getContext(directive) !.native as never as Element;
|
||||
return getLContext(directive) !.native as never as Element;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -221,4 +274,89 @@ export function getHostElement<T>(directive: T): Element {
|
|||
export function getRenderedText(component: any): string {
|
||||
const hostElement = getHostElement(component);
|
||||
return hostElement.textContent || '';
|
||||
}
|
||||
}
|
||||
|
||||
export function loadLContextFromNode(node: Node): LContext {
|
||||
if (!(node instanceof Node)) throw new Error('Expecting instance of DOM Node');
|
||||
return loadLContext(node) !;
|
||||
}
|
||||
|
||||
export interface Listener {
|
||||
name: string;
|
||||
element: Element;
|
||||
callback: (value: any) => any;
|
||||
useCapture: boolean|null;
|
||||
}
|
||||
|
||||
export function isBrowserEvents(listener: Listener): boolean {
|
||||
// Browser events are those which don't have `useCapture` as boolean.
|
||||
return typeof listener.useCapture === 'boolean';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves a list of DOM listeners.
|
||||
*
|
||||
* ```
|
||||
* <my-app>
|
||||
* #VIEW
|
||||
* <div (click)="doSomething()">
|
||||
* </div>
|
||||
* </mp-app>
|
||||
*
|
||||
* expect(getListeners(<div>)).toEqual({
|
||||
* name: 'click',
|
||||
* element: <div>,
|
||||
* callback: () => doSomething(),
|
||||
* useCapture: false
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @param element Element for which the DOM listeners should be retrieved.
|
||||
* @publicApi
|
||||
*/
|
||||
export function getListeners(element: Element): Listener[] {
|
||||
const lContext = loadLContextFromNode(element);
|
||||
const lView = lContext.lView;
|
||||
const tView = lView[TVIEW];
|
||||
const lCleanup = lView[CLEANUP];
|
||||
const tCleanup = tView.cleanup;
|
||||
const listeners: Listener[] = [];
|
||||
if (tCleanup && lCleanup) {
|
||||
for (let i = 0; i < tCleanup.length;) {
|
||||
const firstParam = tCleanup[i++];
|
||||
const secondParam = tCleanup[i++];
|
||||
if (typeof firstParam === 'string') {
|
||||
const name: string = firstParam;
|
||||
const listenerElement: Element = lView[secondParam];
|
||||
const callback: (value: any) => any = lCleanup[tCleanup[i++]];
|
||||
const useCaptureOrIndx = tCleanup[i++];
|
||||
// if useCaptureOrIndx is boolean then report it as is.
|
||||
// if useCaptureOrIndx is positive number then it in unsubscribe method
|
||||
// if useCaptureOrIndx is negative number then it is a Subscription
|
||||
const useCapture = typeof useCaptureOrIndx === 'boolean' ?
|
||||
useCaptureOrIndx :
|
||||
(useCaptureOrIndx >= 0 ? false : null);
|
||||
if (element == listenerElement) {
|
||||
listeners.push({element, name, callback, useCapture});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
listeners.sort(sortListeners);
|
||||
return listeners;
|
||||
}
|
||||
|
||||
function sortListeners(a: Listener, b: Listener) {
|
||||
if (a.name == b.name) return 0;
|
||||
return a.name < b.name ? -1 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function should not exist because it is megamorphic and only mostly correct.
|
||||
*
|
||||
* See call site for more info.
|
||||
*/
|
||||
function isDirectiveDefHack(obj: any): obj is DirectiveDef<any> {
|
||||
return obj.type !== undefined && obj.template !== undefined && obj.declaredInputs !== undefined;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import {global} from '../util';
|
||||
|
||||
import {assertDefined} from './assert';
|
||||
import {getComponent, getDirectives, getHostElement, getInjector, getPlayers, getRootComponents, getViewComponent, markDirty} from './global_utils_api';
|
||||
import {getComponent, getContext, getDirectives, getHostElement, getInjector, getListeners, getPlayers, getRootComponents, getViewComponent, markDirty} from './global_utils_api';
|
||||
|
||||
|
||||
|
||||
|
@ -41,6 +41,8 @@ export function publishDefaultGlobalUtils() {
|
|||
if (!_published) {
|
||||
_published = true;
|
||||
publishGlobalUtil('getComponent', getComponent);
|
||||
publishGlobalUtil('getContext', getContext);
|
||||
publishGlobalUtil('getListeners', getListeners);
|
||||
publishGlobalUtil('getViewComponent', getViewComponent);
|
||||
publishGlobalUtil('getHostElement', getHostElement);
|
||||
publishGlobalUtil('getInjector', getInjector);
|
||||
|
|
|
@ -15,6 +15,6 @@
|
|||
* file in the public_api_guard test.
|
||||
*/
|
||||
|
||||
export {getComponent, getDirectives, getHostElement, getInjector, getRootComponents, getViewComponent} from './discovery_utils';
|
||||
export {getComponent, getContext, getDirectives, getHostElement, getInjector, getListeners, getRootComponents, getViewComponent} from './discovery_utils';
|
||||
export {markDirty} from './instructions';
|
||||
export {getPlayers} from './players';
|
||||
|
|
|
@ -8,10 +8,11 @@
|
|||
|
||||
import {assertEqual} from './assert';
|
||||
import {DirectiveDef} from './interfaces/definition';
|
||||
import {TNodeFlags} from './interfaces/node';
|
||||
import {TNode} from './interfaces/node';
|
||||
import {FLAGS, HookData, LView, LViewFlags, TView} from './interfaces/view';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* If this is the first template pass, any ngOnInit or ngDoCheck hooks will be queued into
|
||||
* TView.initHooks during directiveCreate.
|
||||
|
@ -42,16 +43,12 @@ export function queueInitHooks(
|
|||
* Loops through the directives on a node and queues all their hooks except ngOnInit
|
||||
* and ngDoCheck, which are queued separately in directiveCreate.
|
||||
*/
|
||||
export function queueLifecycleHooks(flags: number, tView: TView): void {
|
||||
export function queueLifecycleHooks(tView: TView, tNode: TNode): void {
|
||||
if (tView.firstTemplatePass) {
|
||||
const start = flags >> TNodeFlags.DirectiveStartingIndexShift;
|
||||
const count = flags & TNodeFlags.DirectiveCountMask;
|
||||
const end = start + count;
|
||||
|
||||
// It's necessary to loop through the directives at elementEnd() (rather than processing in
|
||||
// directiveCreate) so we can preserve the current hook order. Content, view, and destroy
|
||||
// hooks for projected components and directives must be called *before* their hosts.
|
||||
for (let i = start; i < end; i++) {
|
||||
for (let i = tNode.directiveStart, end = tNode.directiveEnd; i < end; i++) {
|
||||
const def = tView.data[i] as DirectiveDef<any>;
|
||||
queueContentHooks(def, tView, i);
|
||||
queueViewHooks(def, tView, i);
|
||||
|
|
|
@ -25,7 +25,7 @@ import {throwMultipleComponentError} from './errors';
|
|||
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks';
|
||||
import {ACTIVE_INDEX, LContainer, VIEWS} from './interfaces/container';
|
||||
import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
|
||||
import {INJECTOR_SIZE, NodeInjectorFactory} from './interfaces/injector';
|
||||
import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from './interfaces/injector';
|
||||
import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode, TViewNode} from './interfaces/node';
|
||||
import {PlayerFactory} from './interfaces/player';
|
||||
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
||||
|
@ -112,7 +112,7 @@ export function setHostBindings(tView: TView, viewData: LView): void {
|
|||
currentElementIndex = -instruction;
|
||||
// Injector block and providers are taken into account.
|
||||
const providerCount = (tView.expandoInstructions[++i] as number);
|
||||
bindingRootIndex += INJECTOR_SIZE + providerCount;
|
||||
bindingRootIndex += INJECTOR_BLOOM_PARENT_SIZE + providerCount;
|
||||
|
||||
currentDirectiveIndex = bindingRootIndex;
|
||||
} else {
|
||||
|
@ -215,6 +215,7 @@ export function createNodeAtIndex(
|
|||
if (tNode == null) {
|
||||
const previousOrParentTNode = getPreviousOrParentTNode();
|
||||
const isParent = getIsParent();
|
||||
// TODO(misko): Refactor createTNode so that it does not depend on LView.
|
||||
tNode = tView.data[adjustedIndex] = createTNode(lView, type, adjustedIndex, name, attrs, null);
|
||||
|
||||
// Now link ourselves into the tree.
|
||||
|
@ -246,10 +247,7 @@ export function createViewNode(index: number, view: LView) {
|
|||
view[TVIEW].node = createTNode(view, TNodeType.View, index, null, null, null) as TViewNode;
|
||||
}
|
||||
|
||||
setIsParent(true);
|
||||
const tNode = view[TVIEW].node as TViewNode;
|
||||
setPreviousOrParentTNode(tNode);
|
||||
return view[HOST_NODE] = tNode;
|
||||
return view[HOST_NODE] = view[TVIEW].node as TViewNode;
|
||||
}
|
||||
|
||||
|
||||
|
@ -526,7 +524,7 @@ export function elementContainerEnd(): void {
|
|||
lView[QUERIES] = currentQueries.addNode(previousOrParentTNode as TElementContainerNode);
|
||||
}
|
||||
|
||||
queueLifecycleHooks(previousOrParentTNode.flags, tView);
|
||||
queueLifecycleHooks(tView, previousOrParentTNode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -800,6 +798,9 @@ export function listener(
|
|||
eventName: string, listenerFn: (e?: any) => any, useCapture = false): void {
|
||||
const lView = getLView();
|
||||
const tNode = getPreviousOrParentTNode();
|
||||
const tView = lView[TVIEW];
|
||||
const firstTemplatePass = tView.firstTemplatePass;
|
||||
const tCleanup: false|any[] = firstTemplatePass && (tView.cleanup || (tView.cleanup = []));
|
||||
ngDevMode && assertNodeOfPossibleTypes(
|
||||
tNode, TNodeType.Element, TNodeType.Container, TNodeType.ElementContainer);
|
||||
|
||||
|
@ -808,47 +809,45 @@ export function listener(
|
|||
const native = getNativeByTNode(tNode, lView) as RElement;
|
||||
ngDevMode && ngDevMode.rendererAddEventListener++;
|
||||
const renderer = lView[RENDERER];
|
||||
const lCleanup = getCleanup(lView);
|
||||
const lCleanupIndex = lCleanup.length;
|
||||
let useCaptureOrSubIdx: boolean|number = useCapture;
|
||||
|
||||
// In order to match current behavior, native DOM event listeners must be added for all
|
||||
// events (including outputs).
|
||||
if (isProceduralRenderer(renderer)) {
|
||||
const cleanupFn = renderer.listen(native, eventName, listenerFn);
|
||||
storeCleanupFn(lView, cleanupFn);
|
||||
lCleanup.push(listenerFn, cleanupFn);
|
||||
useCaptureOrSubIdx = lCleanupIndex + 1;
|
||||
} else {
|
||||
const wrappedListener = wrapListenerWithPreventDefault(listenerFn);
|
||||
native.addEventListener(eventName, wrappedListener, useCapture);
|
||||
const cleanupInstances = getCleanup(lView);
|
||||
cleanupInstances.push(wrappedListener);
|
||||
if (getFirstTemplatePass()) {
|
||||
getTViewCleanup(lView).push(
|
||||
eventName, tNode.index, cleanupInstances !.length - 1, useCapture);
|
||||
}
|
||||
lCleanup.push(wrappedListener);
|
||||
}
|
||||
tCleanup && tCleanup.push(eventName, tNode.index, lCleanupIndex, useCaptureOrSubIdx);
|
||||
}
|
||||
|
||||
// subscribe to directive outputs
|
||||
if (tNode.outputs === undefined) {
|
||||
// if we create TNode here, inputs must be undefined so we know they still need to be
|
||||
// checked
|
||||
tNode.outputs = generatePropertyAliases(tNode.flags, BindingDirection.Output);
|
||||
tNode.outputs = generatePropertyAliases(tNode, BindingDirection.Output);
|
||||
}
|
||||
|
||||
const outputs = tNode.outputs;
|
||||
let outputData: PropertyAliasValue|undefined;
|
||||
if (outputs && (outputData = outputs[eventName])) {
|
||||
createOutput(lView, outputData, listenerFn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through the outputs associated with a particular event name and subscribes to
|
||||
* each output.
|
||||
*/
|
||||
function createOutput(lView: LView, outputs: PropertyAliasValue, listener: Function): void {
|
||||
for (let i = 0; i < outputs.length; i += 2) {
|
||||
ngDevMode && assertDataInRange(lView, outputs[i] as number);
|
||||
const subscription = lView[outputs[i] as number][outputs[i + 1]].subscribe(listener);
|
||||
storeCleanupWithContext(lView, subscription, subscription.unsubscribe);
|
||||
let props: PropertyAliasValue|undefined;
|
||||
if (outputs && (props = outputs[eventName])) {
|
||||
const propsLength = props.length;
|
||||
if (propsLength) {
|
||||
const lCleanup = getCleanup(lView);
|
||||
for (let i = 0; i < propsLength; i += 2) {
|
||||
ngDevMode && assertDataInRange(lView, props[i] as number);
|
||||
const subscription = lView[props[i] as number][props[i + 1]].subscribe(listenerFn);
|
||||
const idx = lCleanup.length;
|
||||
lCleanup.push(listenerFn, subscription);
|
||||
tCleanup && tCleanup.push(eventName, tNode.index, idx, -(idx + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -860,10 +859,11 @@ function createOutput(lView: LView, outputs: PropertyAliasValue, listener: Funct
|
|||
* - Index of context we just saved in LView.cleanupInstances
|
||||
*/
|
||||
export function storeCleanupWithContext(lView: LView, context: any, cleanupFn: Function): void {
|
||||
getCleanup(lView).push(context);
|
||||
const lCleanup = getCleanup(lView);
|
||||
lCleanup.push(context);
|
||||
|
||||
if (lView[TVIEW].firstTemplatePass) {
|
||||
getTViewCleanup(lView).push(cleanupFn, lView[CLEANUP] !.length - 1);
|
||||
getTViewCleanup(lView).push(cleanupFn, lCleanup.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -900,7 +900,7 @@ export function elementEnd(): void {
|
|||
lView[QUERIES] = currentQueries.addNode(previousOrParentTNode as TElementNode);
|
||||
}
|
||||
|
||||
queueLifecycleHooks(previousOrParentTNode.flags, getLView()[TVIEW]);
|
||||
queueLifecycleHooks(getLView()[TVIEW], previousOrParentTNode);
|
||||
decreaseElementDepthCount();
|
||||
}
|
||||
|
||||
|
@ -984,7 +984,7 @@ export function elementProperty<T>(
|
|||
* @returns the TNode object
|
||||
*/
|
||||
export function createTNode(
|
||||
viewData: LView, type: TNodeType, adjustedIndex: number, tagName: string | null,
|
||||
lView: LView, type: TNodeType, adjustedIndex: number, tagName: string | null,
|
||||
attrs: TAttributes | null, tViews: TView[] | null): TNode {
|
||||
const previousOrParentTNode = getPreviousOrParentTNode();
|
||||
ngDevMode && ngDevMode.tNode++;
|
||||
|
@ -993,13 +993,15 @@ export function createTNode(
|
|||
|
||||
// Parents cannot cross component boundaries because components will be used in multiple places,
|
||||
// so it's only set if the view is the same.
|
||||
const parentInSameView = parent && viewData && parent !== viewData[HOST_NODE];
|
||||
const parentInSameView = parent && lView && parent !== lView[HOST_NODE];
|
||||
const tParent = parentInSameView ? parent as TElementNode | TContainerNode : null;
|
||||
|
||||
return {
|
||||
type: type,
|
||||
index: adjustedIndex,
|
||||
injectorIndex: tParent ? tParent.injectorIndex : -1,
|
||||
directiveStart: -1,
|
||||
directiveEnd: -1,
|
||||
flags: 0,
|
||||
providerIndexes: 0,
|
||||
tagName: tagName,
|
||||
|
@ -1044,15 +1046,13 @@ function setNgReflectProperties(lView: LView, element: RElement, propName: strin
|
|||
* @param Direction direction whether to consider inputs or outputs
|
||||
* @returns PropertyAliases|null aggregate of all properties if any, `null` otherwise
|
||||
*/
|
||||
function generatePropertyAliases(
|
||||
tNodeFlags: TNodeFlags, direction: BindingDirection): PropertyAliases|null {
|
||||
function generatePropertyAliases(tNode: TNode, direction: BindingDirection): PropertyAliases|null {
|
||||
const tView = getLView()[TVIEW];
|
||||
const count = tNodeFlags & TNodeFlags.DirectiveCountMask;
|
||||
let propStore: PropertyAliases|null = null;
|
||||
const start = tNode.directiveStart;
|
||||
const end = tNode.directiveEnd;
|
||||
|
||||
if (count > 0) {
|
||||
const start = tNodeFlags >> TNodeFlags.DirectiveStartingIndexShift;
|
||||
const end = start + count;
|
||||
if (end > start) {
|
||||
const isInput = direction === BindingDirection.Input;
|
||||
const defs = tView.data;
|
||||
|
||||
|
@ -1093,7 +1093,7 @@ export function elementClassProp(
|
|||
}
|
||||
const val =
|
||||
(value instanceof BoundPlayerFactory) ? (value as BoundPlayerFactory<boolean>) : (!!value);
|
||||
updateElementClassProp(getStylingContext(index, getLView()), classIndex, val);
|
||||
updateElementClassProp(getStylingContext(index + HEADER_OFFSET, getLView()), classIndex, val);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1152,14 +1152,14 @@ export function elementStyling(
|
|||
|
||||
if (styleDeclarations && styleDeclarations.length ||
|
||||
classDeclarations && classDeclarations.length) {
|
||||
const index = tNode.index - HEADER_OFFSET;
|
||||
const index = tNode.index;
|
||||
if (delegateToClassInput(tNode)) {
|
||||
const lView = getLView();
|
||||
const stylingContext = getStylingContext(index, lView);
|
||||
const initialClasses = stylingContext[StylingIndex.PreviousOrCachedMultiClassValue] as string;
|
||||
setInputsForProperty(lView, tNode.inputs !['class'] !, initialClasses);
|
||||
}
|
||||
elementStylingApply(index);
|
||||
elementStylingApply(index - HEADER_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1186,7 +1186,7 @@ export function elementStylingApply(index: number, directive?: {}): void {
|
|||
const lView = getLView();
|
||||
const isFirstRender = (lView[FLAGS] & LViewFlags.CreationMode) !== 0;
|
||||
const totalPlayersQueued = renderStyleAndClassBindings(
|
||||
getStylingContext(index, lView), lView[RENDERER], lView, isFirstRender);
|
||||
getStylingContext(index + HEADER_OFFSET, lView), lView[RENDERER], lView, isFirstRender);
|
||||
if (totalPlayersQueued > 0) {
|
||||
const rootContext = getRootContext(lView);
|
||||
scheduleTick(rootContext, RootContextFlags.FlushPlayers);
|
||||
|
@ -1234,7 +1234,8 @@ export function elementStyleProp(
|
|||
if (directive != undefined) {
|
||||
hackImplementationOfElementStyleProp(index, styleIndex, valueToAdd, suffix, directive);
|
||||
} else {
|
||||
updateElementStyleProp(getStylingContext(index, getLView()), styleIndex, valueToAdd);
|
||||
updateElementStyleProp(
|
||||
getStylingContext(index + HEADER_OFFSET, getLView()), styleIndex, valueToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1268,7 +1269,7 @@ export function elementStylingMap<T>(
|
|||
index, classes, styles, directive); // supported in next PR
|
||||
const lView = getLView();
|
||||
const tNode = getTNode(index, lView);
|
||||
const stylingContext = getStylingContext(index, lView);
|
||||
const stylingContext = getStylingContext(index + HEADER_OFFSET, lView);
|
||||
if (delegateToClassInput(tNode) && classes !== NO_CHANGE) {
|
||||
const initialClasses = stylingContext[StylingIndex.PreviousOrCachedMultiClassValue] as string;
|
||||
const classInputVal =
|
||||
|
@ -1482,27 +1483,26 @@ function resolveDirectives(
|
|||
/**
|
||||
* Instantiate all the directives that were previously resolved on the current node.
|
||||
*/
|
||||
function instantiateAllDirectives(tView: TView, viewData: LView, previousOrParentTNode: TNode) {
|
||||
const start = previousOrParentTNode.flags >> TNodeFlags.DirectiveStartingIndexShift;
|
||||
const end = start + (previousOrParentTNode.flags & TNodeFlags.DirectiveCountMask);
|
||||
function instantiateAllDirectives(tView: TView, lView: LView, tNode: TNode) {
|
||||
const start = tNode.directiveStart;
|
||||
const end = tNode.directiveEnd;
|
||||
if (!getFirstTemplatePass() && start < end) {
|
||||
getOrCreateNodeInjectorForNode(
|
||||
previousOrParentTNode as TElementNode | TContainerNode | TElementContainerNode, viewData);
|
||||
tNode as TElementNode | TContainerNode | TElementContainerNode, lView);
|
||||
}
|
||||
for (let i = start; i < end; i++) {
|
||||
const def = tView.data[i] as DirectiveDef<any>;
|
||||
if (isComponentDef(def)) {
|
||||
addComponentLogic(viewData, previousOrParentTNode, def as ComponentDef<any>);
|
||||
addComponentLogic(lView, tNode, def as ComponentDef<any>);
|
||||
}
|
||||
const directive =
|
||||
getNodeInjectable(tView.data, viewData !, i, previousOrParentTNode as TElementNode);
|
||||
postProcessDirective(viewData, directive, def, i);
|
||||
const directive = getNodeInjectable(tView.data, lView !, i, tNode as TElementNode);
|
||||
postProcessDirective(lView, directive, def, i);
|
||||
}
|
||||
}
|
||||
|
||||
function invokeDirectivesHostBindings(tView: TView, viewData: LView, previousOrParentTNode: TNode) {
|
||||
const start = previousOrParentTNode.flags >> TNodeFlags.DirectiveStartingIndexShift;
|
||||
const end = start + (previousOrParentTNode.flags & TNodeFlags.DirectiveCountMask);
|
||||
function invokeDirectivesHostBindings(tView: TView, viewData: LView, tNode: TNode) {
|
||||
const start = tNode.directiveStart;
|
||||
const end = tNode.directiveEnd;
|
||||
const expando = tView.expandoInstructions !;
|
||||
const firstTemplatePass = getFirstTemplatePass();
|
||||
for (let i = start; i < end; i++) {
|
||||
|
@ -1511,7 +1511,7 @@ function invokeDirectivesHostBindings(tView: TView, viewData: LView, previousOrP
|
|||
if (def.hostBindings) {
|
||||
const previousExpandoLength = expando.length;
|
||||
setCurrentDirectiveDef(def);
|
||||
def.hostBindings !(RenderFlags.Create, directive, previousOrParentTNode.index);
|
||||
def.hostBindings !(RenderFlags.Create, directive, tNode.index);
|
||||
setCurrentDirectiveDef(null);
|
||||
// `hostBindings` function may or may not contain `allocHostVars` call
|
||||
// (e.g. it may not if it only contains host listeners), so we need to check whether
|
||||
|
@ -1715,11 +1715,12 @@ export function initNodeFlags(tNode: TNode, index: number, numberOfDirectives: n
|
|||
'expected node flags to not be initialized');
|
||||
|
||||
ngDevMode && assertNotEqual(
|
||||
numberOfDirectives, TNodeFlags.DirectiveCountMask,
|
||||
numberOfDirectives, tNode.directiveEnd - tNode.directiveStart,
|
||||
'Reached the max number of directives');
|
||||
// When the first directive is created on a node, save the index
|
||||
tNode.flags = index << TNodeFlags.DirectiveStartingIndexShift | flags & TNodeFlags.isComponent |
|
||||
numberOfDirectives;
|
||||
tNode.flags = flags & TNodeFlags.isComponent;
|
||||
tNode.directiveStart = index;
|
||||
tNode.directiveEnd = index + numberOfDirectives;
|
||||
tNode.providerIndexes = index;
|
||||
}
|
||||
|
||||
|
@ -1894,7 +1895,7 @@ export function template(
|
|||
if (currentQueries) {
|
||||
lView[QUERIES] = currentQueries.addNode(previousOrParentTNode as TContainerNode);
|
||||
}
|
||||
queueLifecycleHooks(tNode.flags, tView);
|
||||
queueLifecycleHooks(tView, tNode);
|
||||
setIsParent(false);
|
||||
}
|
||||
|
||||
|
@ -2842,7 +2843,7 @@ function initializeTNodeInputs(tNode: TNode | null) {
|
|||
if (tNode) {
|
||||
if (tNode.inputs === undefined) {
|
||||
// mark inputs as checked
|
||||
tNode.inputs = generatePropertyAliases(tNode.flags, BindingDirection.Input);
|
||||
tNode.inputs = generatePropertyAliases(tNode, BindingDirection.Input);
|
||||
}
|
||||
return tNode.inputs;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import {LView, TData} from './view';
|
|||
|
||||
export const TNODE = 8;
|
||||
export const PARENT_INJECTOR = 8;
|
||||
export const INJECTOR_SIZE = 9;
|
||||
export const INJECTOR_BLOOM_PARENT_SIZE = 9;
|
||||
|
||||
/**
|
||||
* Represents a relative location of parent injector.
|
||||
|
|
|
@ -28,23 +28,17 @@ export const enum TNodeType {
|
|||
* Corresponds to the TNode.flags property.
|
||||
*/
|
||||
export const enum TNodeFlags {
|
||||
/** The number of directives on this node is encoded on the least significant bits */
|
||||
DirectiveCountMask = 0b00000000000000000000111111111111,
|
||||
|
||||
/** This bit is set if the node is a component */
|
||||
isComponent = 0b00000000000000000001000000000000,
|
||||
isComponent = 0b0001,
|
||||
|
||||
/** This bit is set if the node has been projected */
|
||||
isProjected = 0b00000000000000000010000000000000,
|
||||
isProjected = 0b0010,
|
||||
|
||||
/** This bit is set if the node has any content queries */
|
||||
hasContentQuery = 0b00000000000000000100000000000000,
|
||||
hasContentQuery = 0b0100,
|
||||
|
||||
/** This bit is set if the node has any directives that contain [class properties */
|
||||
hasClassInput = 0b00000000000000001000000000000000,
|
||||
|
||||
/** The index of the first directive on this node is encoded on the most significant bits */
|
||||
DirectiveStartingIndexShift = 16,
|
||||
hasClassInput = 0b1000,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -128,13 +122,17 @@ export interface TNode {
|
|||
injectorIndex: number;
|
||||
|
||||
/**
|
||||
* This number stores two values using its bits:
|
||||
*
|
||||
* - the number of directives on that node (first 12 bits)
|
||||
* - the starting index of the node's directives in the directives array (last 20 bits).
|
||||
*
|
||||
* These two values are necessary so DI can effectively search the directives associated
|
||||
* with a node without searching the whole directives array.
|
||||
* Stores starting index of the directives.
|
||||
*/
|
||||
directiveStart: number;
|
||||
|
||||
/**
|
||||
* Stores final exclusive index of the directives.
|
||||
*/
|
||||
directiveEnd: number;
|
||||
|
||||
/**
|
||||
* Stores if Node isComponent, isProjected, hasContentQuery and hasClassInput
|
||||
*/
|
||||
flags: TNodeFlags;
|
||||
|
||||
|
@ -144,6 +142,7 @@ export interface TNode {
|
|||
* - the index of the first provider on that node (first 16 bits)
|
||||
* - the count of view providers from the component on this node (last 16 bits)
|
||||
*/
|
||||
// TODO(misko): break this into actual vars.
|
||||
providerIndexes: TNodeProviderIndexes;
|
||||
|
||||
/** The tag name associated with this node. */
|
||||
|
|
|
@ -446,19 +446,25 @@ export interface TView {
|
|||
* saves on memory (70 bytes per array) and on a few bytes of code size (for two
|
||||
* separate for loops).
|
||||
*
|
||||
* If it's a native DOM listener being stored:
|
||||
* 1st index is: event name to remove
|
||||
* 2nd index is: index of native element in LView.data[]
|
||||
* 3rd index is: index of wrapped listener function in LView.cleanupInstances[]
|
||||
* 4th index is: useCapture boolean
|
||||
* If it's a native DOM listener or output subscription being stored:
|
||||
* 1st index is: event name `name = tView.cleanup[i+0]`
|
||||
* 2nd index is: index of native element `element = lView[tView.cleanup[i+1]]`
|
||||
* 3rd index is: index of listener function `listener = lView[CLEANUP][tView.cleanup[i+2]]`
|
||||
* 4th index is: `useCaptureOrIndx = tView.cleanup[i+3]`
|
||||
* `typeof useCaptureOrIndx == 'boolean' : useCapture boolean
|
||||
* `typeof useCaptureOrIndx == 'number':
|
||||
* `useCaptureOrIndx >= 0` `removeListener = LView[CLEANUP][useCaptureOrIndx]`
|
||||
* `useCaptureOrIndx < 0` `subscription = LView[CLEANUP][-useCaptureOrIndx]`
|
||||
*
|
||||
* If it's a renderer2 style listener or ViewRef destroy hook being stored:
|
||||
* 1st index is: index of the cleanup function in LView.cleanupInstances[]
|
||||
* 2nd index is: null
|
||||
* 2nd index is: `null`
|
||||
* `lView[CLEANUP][tView.cleanup[i+0]]()`
|
||||
*
|
||||
* If it's an output subscription or query list destroy hook:
|
||||
* 1st index is: output unsubscribe function / query list destroy function
|
||||
* 2nd index is: index of function context in LView.cleanupInstances[]
|
||||
* `tView.cleanup[i+0].call(lView[CLEANUP][tView.cleanup[i+1]])`
|
||||
*/
|
||||
cleanup: any[]|null;
|
||||
|
||||
|
|
|
@ -473,23 +473,37 @@ function cleanUpView(viewOrContainer: LView | LContainer): void {
|
|||
|
||||
/** Removes listeners and unsubscribes from output subscriptions */
|
||||
function removeListeners(lView: LView): void {
|
||||
const cleanup = lView[TVIEW].cleanup !;
|
||||
if (cleanup != null) {
|
||||
for (let i = 0; i < cleanup.length - 1; i += 2) {
|
||||
if (typeof cleanup[i] === 'string') {
|
||||
const tCleanup = lView[TVIEW].cleanup !;
|
||||
if (tCleanup != null) {
|
||||
const lCleanup = lView[CLEANUP] !;
|
||||
for (let i = 0; i < tCleanup.length - 1; i += 2) {
|
||||
if (typeof tCleanup[i] === 'string') {
|
||||
// This is a listener with the native renderer
|
||||
const native = readElementValue(lView[cleanup[i + 1]]);
|
||||
const listener = lView[CLEANUP] ![cleanup[i + 2]];
|
||||
native.removeEventListener(cleanup[i], listener, cleanup[i + 3]);
|
||||
const idx = tCleanup[i + 1];
|
||||
const listener = lCleanup[tCleanup[i + 2]];
|
||||
const native = readElementValue(lView[idx]);
|
||||
const useCaptureOrSubIdx = tCleanup[i + 3];
|
||||
if (typeof useCaptureOrSubIdx === 'boolean') {
|
||||
// DOM listener
|
||||
native.removeEventListener(tCleanup[i], listener, useCaptureOrSubIdx);
|
||||
} else {
|
||||
if (useCaptureOrSubIdx >= 0) {
|
||||
// unregister
|
||||
lCleanup[useCaptureOrSubIdx]();
|
||||
} else {
|
||||
// Subscription
|
||||
lCleanup[-useCaptureOrSubIdx].unsubscribe();
|
||||
}
|
||||
}
|
||||
i += 2;
|
||||
} else if (typeof cleanup[i] === 'number') {
|
||||
} else if (typeof tCleanup[i] === 'number') {
|
||||
// This is a listener with renderer2 (cleanup fn can be found by index)
|
||||
const cleanupFn = lView[CLEANUP] ![cleanup[i]];
|
||||
const cleanupFn = lCleanup[tCleanup[i]];
|
||||
cleanupFn();
|
||||
} else {
|
||||
// This is a cleanup function that is grouped with the index of its context
|
||||
const context = lView[CLEANUP] ![cleanup[i + 1]];
|
||||
cleanup[i].call(context);
|
||||
const context = lCleanup[tCleanup[i + 1]];
|
||||
tCleanup[i].call(context);
|
||||
}
|
||||
}
|
||||
lView[CLEANUP] = null;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
import './ng_dev_mode';
|
||||
|
||||
import {getContext} from './context_discovery';
|
||||
import {getLContext} from './context_discovery';
|
||||
import {getRootContext} from './discovery_utils';
|
||||
import {scheduleTick} from './instructions';
|
||||
import {ComponentInstance, DirectiveInstance, Player} from './interfaces/player';
|
||||
|
@ -29,7 +29,7 @@ import {addPlayerInternal, getOrCreatePlayerContext, getPlayerContext, getPlayer
|
|||
*/
|
||||
export function addPlayer(
|
||||
ref: ComponentInstance | DirectiveInstance | HTMLElement, player: Player): void {
|
||||
const context = getContext(ref);
|
||||
const context = getLContext(ref);
|
||||
if (!context) {
|
||||
ngDevMode && throwInvalidRefError();
|
||||
return;
|
||||
|
@ -54,13 +54,13 @@ export function addPlayer(
|
|||
* @publicApi
|
||||
*/
|
||||
export function getPlayers(ref: ComponentInstance | DirectiveInstance | HTMLElement): Player[] {
|
||||
const context = getContext(ref);
|
||||
const context = getLContext(ref);
|
||||
if (!context) {
|
||||
ngDevMode && throwInvalidRefError();
|
||||
return [];
|
||||
}
|
||||
|
||||
const stylingContext = getStylingContext(context.nodeIndex - HEADER_OFFSET, context.lView);
|
||||
const stylingContext = getStylingContext(context.nodeIndex, context.lView);
|
||||
const playerContext = stylingContext ? getPlayerContext(stylingContext) : null;
|
||||
return playerContext ? getPlayersInternal(playerContext) : [];
|
||||
}
|
||||
|
|
|
@ -668,7 +668,7 @@ function isDirty(context: StylingContext, index: number): boolean {
|
|||
return ((context[adjustedIndex] as number) & StylingFlags.Dirty) == StylingFlags.Dirty;
|
||||
}
|
||||
|
||||
function isClassBased(context: StylingContext, index: number): boolean {
|
||||
export function isClassBased(context: StylingContext, index: number): boolean {
|
||||
const adjustedIndex =
|
||||
index >= StylingIndex.SingleStylesStartPosition ? (index + StylingIndex.FlagsOffset) : index;
|
||||
return ((context[adjustedIndex] as number) & StylingFlags.Class) == StylingFlags.Class;
|
||||
|
@ -776,11 +776,11 @@ function getPointers(context: StylingContext, index: number): number {
|
|||
return context[adjustedIndex] as number;
|
||||
}
|
||||
|
||||
function getValue(context: StylingContext, index: number): string|boolean|null {
|
||||
export function getValue(context: StylingContext, index: number): string|boolean|null {
|
||||
return context[index + StylingIndex.ValueOffset] as string | boolean | null;
|
||||
}
|
||||
|
||||
function getProp(context: StylingContext, index: number): string {
|
||||
export function getProp(context: StylingContext, index: number): string {
|
||||
return context[index + StylingIndex.PropertyOffset] as string;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import '../ng_dev_mode';
|
||||
|
||||
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
|
||||
import {getContext} from '../context_discovery';
|
||||
import {getLContext} from '../context_discovery';
|
||||
import {ACTIVE_INDEX, LContainer} from '../interfaces/container';
|
||||
import {LContext} from '../interfaces/context';
|
||||
import {PlayState, Player, PlayerContext, PlayerIndex} from '../interfaces/player';
|
||||
|
@ -60,7 +60,7 @@ export function allocStylingContext(
|
|||
* @param viewData The view to search for the styling context
|
||||
*/
|
||||
export function getStylingContext(index: number, viewData: LView): StylingContext {
|
||||
let storageIndex = index + HEADER_OFFSET;
|
||||
let storageIndex = index;
|
||||
let slotValue: LContainer|LView|StylingContext|RElement = viewData[storageIndex];
|
||||
let wrapper: LContainer|LView|StylingContext = viewData;
|
||||
|
||||
|
@ -73,7 +73,7 @@ export function getStylingContext(index: number, viewData: LView): StylingContex
|
|||
return wrapper as StylingContext;
|
||||
} else {
|
||||
// This is an LView or an LContainer
|
||||
const stylingTemplate = getTNode(index, viewData).stylingTemplate;
|
||||
const stylingTemplate = getTNode(index - HEADER_OFFSET, viewData).stylingTemplate;
|
||||
|
||||
if (wrapper !== viewData) {
|
||||
storageIndex = HOST;
|
||||
|
@ -85,9 +85,10 @@ export function getStylingContext(index: number, viewData: LView): StylingContex
|
|||
}
|
||||
}
|
||||
|
||||
function isStylingContext(value: LView | LContainer | StylingContext) {
|
||||
export function isStylingContext(value: any): value is StylingContext {
|
||||
// Not an LView or an LContainer
|
||||
return typeof value[FLAGS] !== 'number' && typeof value[ACTIVE_INDEX] !== 'number';
|
||||
return Array.isArray(value) && typeof value[FLAGS] !== 'number' &&
|
||||
typeof value[ACTIVE_INDEX] !== 'number';
|
||||
}
|
||||
|
||||
export function addPlayerInternal(
|
||||
|
@ -152,14 +153,14 @@ export function getPlayersInternal(playerContext: PlayerContext): Player[] {
|
|||
|
||||
export function getOrCreatePlayerContext(target: {}, context?: LContext | null): PlayerContext|
|
||||
null {
|
||||
context = context || getContext(target) !;
|
||||
context = context || getLContext(target) !;
|
||||
if (!context) {
|
||||
ngDevMode && throwInvalidRefError();
|
||||
return null;
|
||||
}
|
||||
|
||||
const {lView, nodeIndex} = context;
|
||||
const stylingContext = getStylingContext(nodeIndex - HEADER_OFFSET, lView);
|
||||
const stylingContext = getStylingContext(nodeIndex, lView);
|
||||
return getPlayerContext(stylingContext) || allocPlayerContext(stylingContext);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import {global} from '../util';
|
||||
|
||||
import {assertDataInRange, assertDefined} from './assert';
|
||||
import {assertDataInRange, assertDefined, assertGreaterThan, assertLessThan} from './assert';
|
||||
import {ACTIVE_INDEX, LContainer} from './interfaces/container';
|
||||
import {LContext, MONKEY_PATCH_KEY_NAME} from './interfaces/context';
|
||||
import {ComponentDef, DirectiveDef} from './interfaces/definition';
|
||||
|
@ -100,6 +100,8 @@ export function getNativeByTNode(tNode: TNode, hostView: LView): RElement|RText|
|
|||
}
|
||||
|
||||
export function getTNode(index: number, view: LView): TNode {
|
||||
ngDevMode && assertGreaterThan(index, -1, 'wrong index for TNode');
|
||||
ngDevMode && assertLessThan(index, view[TVIEW].data.length, 'wrong index for TNode');
|
||||
return view[TVIEW].data[index + HEADER_OFFSET] as TNode;
|
||||
}
|
||||
|
||||
|
@ -157,6 +159,7 @@ export function getRootContext(viewOrComponent: LView | {}): RootContext {
|
|||
* a component, directive or a DOM node).
|
||||
*/
|
||||
export function readPatchedData(target: any): LView|LContext|null {
|
||||
ngDevMode && assertDefined(target, 'Target expected');
|
||||
return target[MONKEY_PATCH_KEY_NAME];
|
||||
}
|
||||
|
||||
|
|
|
@ -355,7 +355,7 @@ export function injectChangeDetectorRef(): ViewEngine_ChangeDetectorRef {
|
|||
export function createViewRef(
|
||||
hostTNode: TNode, hostView: LView, context: any): ViewEngine_ChangeDetectorRef {
|
||||
if (isComponent(hostTNode)) {
|
||||
const componentIndex = hostTNode.flags >> TNodeFlags.DirectiveStartingIndexShift;
|
||||
const componentIndex = hostTNode.directiveStart;
|
||||
const componentView = getComponentViewByIndex(hostTNode.index, hostView);
|
||||
return new ViewRef(componentView, context, componentIndex);
|
||||
} else if (hostTNode.type === TNodeType.Element) {
|
||||
|
|
|
@ -12,7 +12,7 @@ import {stringify} from '../render3/util';
|
|||
|
||||
import {BypassType, allowSanitizationBypass} from './bypass';
|
||||
import {_sanitizeHtml as _sanitizeHtml} from './html_sanitizer';
|
||||
import {SecurityContext} from './security';
|
||||
import {Sanitizer, SecurityContext} from './security';
|
||||
import {StyleSanitizeFn, _sanitizeStyle as _sanitizeStyle} from './style_sanitizer';
|
||||
import {_sanitizeUrl as _sanitizeUrl} from './url_sanitizer';
|
||||
|
||||
|
@ -32,7 +32,7 @@ import {_sanitizeUrl as _sanitizeUrl} from './url_sanitizer';
|
|||
* and urls have been removed.
|
||||
*/
|
||||
export function sanitizeHtml(unsafeHtml: any): string {
|
||||
const sanitizer = getLView()[SANITIZER];
|
||||
const sanitizer = getSanitizer();
|
||||
if (sanitizer) {
|
||||
return sanitizer.sanitize(SecurityContext.HTML, unsafeHtml) || '';
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ export function sanitizeHtml(unsafeHtml: any): string {
|
|||
* dangerous javascript and urls have been removed.
|
||||
*/
|
||||
export function sanitizeStyle(unsafeStyle: any): string {
|
||||
const sanitizer = getLView()[SANITIZER];
|
||||
const sanitizer = getSanitizer();
|
||||
if (sanitizer) {
|
||||
return sanitizer.sanitize(SecurityContext.STYLE, unsafeStyle) || '';
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ export function sanitizeStyle(unsafeStyle: any): string {
|
|||
* all of the dangerous javascript has been removed.
|
||||
*/
|
||||
export function sanitizeUrl(unsafeUrl: any): string {
|
||||
const sanitizer = getLView()[SANITIZER];
|
||||
const sanitizer = getSanitizer();
|
||||
if (sanitizer) {
|
||||
return sanitizer.sanitize(SecurityContext.URL, unsafeUrl) || '';
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ export function sanitizeUrl(unsafeUrl: any): string {
|
|||
* only trusted `url`s have been allowed to pass.
|
||||
*/
|
||||
export function sanitizeResourceUrl(unsafeResourceUrl: any): string {
|
||||
const sanitizer = getLView()[SANITIZER];
|
||||
const sanitizer = getSanitizer();
|
||||
if (sanitizer) {
|
||||
return sanitizer.sanitize(SecurityContext.RESOURCE_URL, unsafeResourceUrl) || '';
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ export function sanitizeResourceUrl(unsafeResourceUrl: any): string {
|
|||
* because only trusted `scripts` have been allowed to pass.
|
||||
*/
|
||||
export function sanitizeScript(unsafeScript: any): string {
|
||||
const sanitizer = getLView()[SANITIZER];
|
||||
const sanitizer = getSanitizer();
|
||||
if (sanitizer) {
|
||||
return sanitizer.sanitize(SecurityContext.SCRIPT, unsafeScript) || '';
|
||||
}
|
||||
|
@ -145,3 +145,8 @@ export const defaultStyleSanitizer = (function(prop: string, value?: string): st
|
|||
|
||||
return sanitizeStyle(value);
|
||||
} as StyleSanitizeFn);
|
||||
|
||||
function getSanitizer(): Sanitizer|null {
|
||||
const lView = getLView();
|
||||
return lView && lView[SANITIZER];
|
||||
}
|
|
@ -84,7 +84,7 @@
|
|||
"name": "INJECTOR"
|
||||
},
|
||||
{
|
||||
"name": "INJECTOR_SIZE"
|
||||
"name": "INJECTOR_BLOOM_PARENT_SIZE"
|
||||
},
|
||||
{
|
||||
"name": "InjectFlags"
|
||||
|
@ -416,9 +416,6 @@
|
|||
{
|
||||
"name": "createNodeAtIndex"
|
||||
},
|
||||
{
|
||||
"name": "createOutput"
|
||||
},
|
||||
{
|
||||
"name": "createRootComponent"
|
||||
},
|
||||
|
@ -605,21 +602,12 @@
|
|||
{
|
||||
"name": "getContainerRenderParent"
|
||||
},
|
||||
{
|
||||
"name": "getContext"
|
||||
},
|
||||
{
|
||||
"name": "getCreationMode"
|
||||
},
|
||||
{
|
||||
"name": "getDirectiveDef"
|
||||
},
|
||||
{
|
||||
"name": "getDirectiveEndIndex"
|
||||
},
|
||||
{
|
||||
"name": "getDirectiveStartIndex"
|
||||
},
|
||||
{
|
||||
"name": "getDirectivesAtNodeIndex"
|
||||
},
|
||||
|
@ -656,6 +644,9 @@
|
|||
{
|
||||
"name": "getLContainer"
|
||||
},
|
||||
{
|
||||
"name": "getLContext"
|
||||
},
|
||||
{
|
||||
"name": "getLView"
|
||||
},
|
||||
|
@ -924,7 +915,7 @@
|
|||
"name": "listener"
|
||||
},
|
||||
{
|
||||
"name": "loadContext"
|
||||
"name": "loadLContext"
|
||||
},
|
||||
{
|
||||
"name": "locateDirectiveOrProvider"
|
||||
|
@ -1148,9 +1139,6 @@
|
|||
{
|
||||
"name": "storeCleanupFn"
|
||||
},
|
||||
{
|
||||
"name": "storeCleanupWithContext"
|
||||
},
|
||||
{
|
||||
"name": "stringify"
|
||||
},
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
"name": "INJECTOR"
|
||||
},
|
||||
{
|
||||
"name": "INJECTOR_SIZE"
|
||||
"name": "INJECTOR_BLOOM_PARENT_SIZE"
|
||||
},
|
||||
{
|
||||
"name": "MONKEY_PATCH_KEY_NAME"
|
||||
|
|
|
@ -171,7 +171,7 @@
|
|||
"name": "INJECTOR$1"
|
||||
},
|
||||
{
|
||||
"name": "INJECTOR_SIZE"
|
||||
"name": "INJECTOR_BLOOM_PARENT_SIZE"
|
||||
},
|
||||
{
|
||||
"name": "Inject"
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
"name": "INJECTOR"
|
||||
},
|
||||
{
|
||||
"name": "INJECTOR_SIZE"
|
||||
"name": "INJECTOR_BLOOM_PARENT_SIZE"
|
||||
},
|
||||
{
|
||||
"name": "InjectFlags"
|
||||
|
@ -473,9 +473,6 @@
|
|||
{
|
||||
"name": "createNodeAtIndex"
|
||||
},
|
||||
{
|
||||
"name": "createOutput"
|
||||
},
|
||||
{
|
||||
"name": "createRootComponent"
|
||||
},
|
||||
|
@ -1169,9 +1166,6 @@
|
|||
{
|
||||
"name": "storeCleanupFn"
|
||||
},
|
||||
{
|
||||
"name": "storeCleanupWithContext"
|
||||
},
|
||||
{
|
||||
"name": "stringify"
|
||||
},
|
||||
|
|
|
@ -381,7 +381,7 @@
|
|||
"name": "INJECTOR$1"
|
||||
},
|
||||
{
|
||||
"name": "INJECTOR_SIZE"
|
||||
"name": "INJECTOR_BLOOM_PARENT_SIZE"
|
||||
},
|
||||
{
|
||||
"name": "INSPECT_GLOBAL_NAME"
|
||||
|
@ -1379,9 +1379,6 @@
|
|||
{
|
||||
"name": "createNodeAtIndex"
|
||||
},
|
||||
{
|
||||
"name": "createOutput"
|
||||
},
|
||||
{
|
||||
"name": "createPlatform"
|
||||
},
|
||||
|
@ -1736,12 +1733,6 @@
|
|||
{
|
||||
"name": "getDirectiveDef"
|
||||
},
|
||||
{
|
||||
"name": "getDirectiveEndIndex"
|
||||
},
|
||||
{
|
||||
"name": "getDirectiveStartIndex"
|
||||
},
|
||||
{
|
||||
"name": "getDirectivesAtNodeIndex"
|
||||
},
|
||||
|
@ -1775,6 +1766,9 @@
|
|||
{
|
||||
"name": "getInjectableDef"
|
||||
},
|
||||
{
|
||||
"name": "getInjectionTokens"
|
||||
},
|
||||
{
|
||||
"name": "getInjector"
|
||||
},
|
||||
|
@ -1790,6 +1784,9 @@
|
|||
{
|
||||
"name": "getLContainer"
|
||||
},
|
||||
{
|
||||
"name": "getLContext"
|
||||
},
|
||||
{
|
||||
"name": "getLView"
|
||||
},
|
||||
|
@ -1799,6 +1796,9 @@
|
|||
{
|
||||
"name": "getLastDefinedValue"
|
||||
},
|
||||
{
|
||||
"name": "getListeners"
|
||||
},
|
||||
{
|
||||
"name": "getLocalRefs"
|
||||
},
|
||||
|
@ -2120,6 +2120,12 @@
|
|||
{
|
||||
"name": "isBlackListedEvent"
|
||||
},
|
||||
{
|
||||
"name": "isBrowserEvents"
|
||||
},
|
||||
{
|
||||
"name": "isClassBased"
|
||||
},
|
||||
{
|
||||
"name": "isComponent"
|
||||
},
|
||||
|
@ -2256,10 +2262,13 @@
|
|||
"name": "listener"
|
||||
},
|
||||
{
|
||||
"name": "loadContext"
|
||||
"name": "loadInternal"
|
||||
},
|
||||
{
|
||||
"name": "loadInternal"
|
||||
"name": "loadLContext"
|
||||
},
|
||||
{
|
||||
"name": "loadLContextFromNode"
|
||||
},
|
||||
{
|
||||
"name": "localeEn"
|
||||
|
@ -2348,9 +2357,6 @@
|
|||
{
|
||||
"name": "noopScope"
|
||||
},
|
||||
{
|
||||
"name": "notImplemented"
|
||||
},
|
||||
{
|
||||
"name": "observable"
|
||||
},
|
||||
|
@ -2621,6 +2627,9 @@
|
|||
{
|
||||
"name": "shouldSearchParent"
|
||||
},
|
||||
{
|
||||
"name": "sortListeners"
|
||||
},
|
||||
{
|
||||
"name": "staticError"
|
||||
},
|
||||
|
@ -2633,9 +2642,6 @@
|
|||
{
|
||||
"name": "storeCleanupFn"
|
||||
},
|
||||
{
|
||||
"name": "storeCleanupWithContext"
|
||||
},
|
||||
{
|
||||
"name": "strToNumber"
|
||||
},
|
||||
|
|
|
@ -13,7 +13,6 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
|||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {fixmeIvy} from '@angular/private/testing';
|
||||
|
||||
@Injectable()
|
||||
class Logger {
|
||||
|
@ -259,35 +258,32 @@ class TestApp {
|
|||
expect(list.children.length).toEqual(3);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-719: DebugElement needs proper implementation of its methods.') &&
|
||||
it('should list element attributes', () => {
|
||||
fixture = TestBed.createComponent(TestApp);
|
||||
fixture.detectChanges();
|
||||
const bankElem = fixture.debugElement.children[0];
|
||||
it('should list element attributes', () => {
|
||||
fixture = TestBed.createComponent(TestApp);
|
||||
fixture.detectChanges();
|
||||
const bankElem = fixture.debugElement.children[0];
|
||||
|
||||
expect(bankElem.attributes['bank']).toEqual('RBC');
|
||||
expect(bankElem.attributes['account']).toEqual('4747');
|
||||
});
|
||||
expect(bankElem.attributes['bank']).toEqual('RBC');
|
||||
expect(bankElem.attributes['account']).toEqual('4747');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-719: DebugElement needs proper implementation of its methods.') &&
|
||||
it('should list element classes', () => {
|
||||
fixture = TestBed.createComponent(TestApp);
|
||||
fixture.detectChanges();
|
||||
const bankElem = fixture.debugElement.children[0];
|
||||
it('should list element classes', () => {
|
||||
fixture = TestBed.createComponent(TestApp);
|
||||
fixture.detectChanges();
|
||||
const bankElem = fixture.debugElement.children[0];
|
||||
|
||||
expect(bankElem.classes['closed']).toBe(true);
|
||||
expect(bankElem.classes['open']).toBe(false);
|
||||
});
|
||||
expect(bankElem.classes['closed']).toBe(true);
|
||||
expect(bankElem.classes['open']).toBe(false);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-719: DebugElement needs proper implementation of its methods.') &&
|
||||
it('should list element styles', () => {
|
||||
fixture = TestBed.createComponent(TestApp);
|
||||
fixture.detectChanges();
|
||||
const bankElem = fixture.debugElement.children[0];
|
||||
it('should list element styles', () => {
|
||||
fixture = TestBed.createComponent(TestApp);
|
||||
fixture.detectChanges();
|
||||
const bankElem = fixture.debugElement.children[0];
|
||||
|
||||
expect(bankElem.styles['width']).toEqual('200px');
|
||||
expect(bankElem.styles['color']).toEqual('red');
|
||||
});
|
||||
expect(bankElem.styles['width']).toEqual('200px');
|
||||
expect(bankElem.styles['color']).toEqual('red');
|
||||
});
|
||||
|
||||
it('should query child elements by css', () => {
|
||||
fixture = TestBed.createComponent(ParentComp);
|
||||
|
@ -312,13 +308,12 @@ class TestApp {
|
|||
expect(getDOM().hasClass(childTestEls[3].nativeElement, 'childnested')).toBe(true);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-719: DebugElement needs proper implementation of its methods.') &&
|
||||
it('should list providerTokens', () => {
|
||||
fixture = TestBed.createComponent(ParentComp);
|
||||
fixture.detectChanges();
|
||||
it('should list providerTokens', () => {
|
||||
fixture = TestBed.createComponent(ParentComp);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.debugElement.providerTokens).toContain(Logger);
|
||||
});
|
||||
expect(fixture.debugElement.providerTokens).toContain(Logger);
|
||||
});
|
||||
|
||||
it('should list locals', () => {
|
||||
fixture = TestBed.createComponent(LocalsComp);
|
||||
|
@ -327,25 +322,22 @@ class TestApp {
|
|||
expect(fixture.debugElement.children[0].references !['alice']).toBeAnInstanceOf(MyDir);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-719: DebugElement needs proper implementation of its methods.') &&
|
||||
it('should allow injecting from the element injector', () => {
|
||||
fixture = TestBed.createComponent(ParentComp);
|
||||
fixture.detectChanges();
|
||||
it('should allow injecting from the element injector', () => {
|
||||
fixture = TestBed.createComponent(ParentComp);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect((<Logger>(fixture.debugElement.children[0].injector.get(Logger))).logs).toEqual([
|
||||
'parent', 'nestedparent', 'child', 'nestedchild'
|
||||
]);
|
||||
});
|
||||
expect((<Logger>(fixture.debugElement.children[0].injector.get(Logger))).logs).toEqual([
|
||||
'parent', 'nestedparent', 'child', 'nestedchild'
|
||||
]);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-719: DebugElement needs proper implementation of its methods.') &&
|
||||
it('should list event listeners', () => {
|
||||
fixture = TestBed.createComponent(EventsComp);
|
||||
fixture.detectChanges();
|
||||
it('should list event listeners', () => {
|
||||
fixture = TestBed.createComponent(EventsComp);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.debugElement.children[0].listeners.length).toEqual(1);
|
||||
expect(fixture.debugElement.children[1].listeners.length).toEqual(1);
|
||||
|
||||
});
|
||||
expect(fixture.debugElement.children[0].listeners.length).toEqual(1);
|
||||
expect(fixture.debugElement.children[1].listeners.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should trigger event handlers', () => {
|
||||
fixture = TestBed.createComponent(EventsComp);
|
||||
|
|
|
@ -5,18 +5,16 @@
|
|||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {ReferenceFilter} from '@angular/compiler';
|
||||
import {createInjector} from '@angular/core';
|
||||
|
||||
import {StaticInjector} from '../../src/di/injector';
|
||||
import {getComponent, getDirectives, getInjector, getLocalRefs, getRootComponents, getViewComponent} from '../../src/render3/discovery_utils';
|
||||
import {getComponent, getContext, getDirectives, getInjectionTokens, getInjector, getListeners, getLocalRefs, getRootComponents, getViewComponent} from '../../src/render3/discovery_utils';
|
||||
import {ProvidersFeature, RenderFlags, defineComponent, defineDirective, getHostElement} from '../../src/render3/index';
|
||||
|
||||
import {container, element, elementEnd, elementStart, elementStyling, elementStylingApply, template, bind, elementProperty, text, textBinding, markDirty} from '../../src/render3/instructions';
|
||||
import {element, elementEnd, elementStart, elementStyling, elementStylingApply, template, bind, elementProperty, text, textBinding, markDirty, listener} from '../../src/render3/instructions';
|
||||
|
||||
import {ComponentFixture} from './render_util';
|
||||
import {NgIf} from './common_with_def';
|
||||
import {getRootContext} from '@angular/core/src/render3/util';
|
||||
|
||||
describe('discovery utils', () => {
|
||||
let fixture: ComponentFixture<MyApp>;
|
||||
|
@ -27,8 +25,10 @@ describe('discovery utils', () => {
|
|||
let span: NodeListOf<Element>;
|
||||
let div: NodeListOf<Element>;
|
||||
let p: NodeListOf<Element>;
|
||||
let log: any[];
|
||||
|
||||
beforeEach(() => {
|
||||
log = [];
|
||||
myApp = [];
|
||||
dirA = [];
|
||||
childComponent = [];
|
||||
|
@ -46,7 +46,7 @@ describe('discovery utils', () => {
|
|||
* ```
|
||||
* <my-app>
|
||||
* <#VIEW>
|
||||
* <span>{{text}}</span>
|
||||
* <span (click)=" log.push($event) ">{{text}}</span>
|
||||
* <div dirA #div #foo="dirA"></div>
|
||||
* <child>
|
||||
* <#VIEW>
|
||||
|
@ -110,6 +110,7 @@ describe('discovery utils', () => {
|
|||
template: (rf: RenderFlags, ctx: MyApp) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'span');
|
||||
listener('click', $event => log.push($event));
|
||||
text(1);
|
||||
elementEnd();
|
||||
element(2, 'div', ['dirA', ''], ['div', '', 'foo', 'dirA']);
|
||||
|
@ -146,6 +147,18 @@ describe('discovery utils', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('getContext', () => {
|
||||
it('should throw when called on non-element', () => {
|
||||
expect(() => getContext(dirA[0] as any)).toThrowError(/Expecting instance of DOM Node/);
|
||||
expect(() => getContext(dirA[1] as any)).toThrowError(/Expecting instance of DOM Node/);
|
||||
});
|
||||
it('should return context from element', () => {
|
||||
expect(getContext<MyApp>(child[0])).toEqual(myApp[0]);
|
||||
expect(getContext<{$implicit: boolean}>(child[2]) !.$implicit).toEqual(true);
|
||||
expect(getContext<Child>(p[0])).toEqual(childComponent[0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getHostElement', () => {
|
||||
it('should return element on component', () => {
|
||||
expect(getHostElement(myApp[0])).toEqual(fixture.hostElement);
|
||||
|
@ -217,7 +230,7 @@ describe('discovery utils', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('localRefs', () => {
|
||||
describe('getLocalRefs', () => {
|
||||
it('should retrieve empty map', () => {
|
||||
expect(getLocalRefs(fixture.hostElement)).toEqual({});
|
||||
expect(getLocalRefs(myApp[0])).toEqual({});
|
||||
|
@ -249,6 +262,30 @@ describe('discovery utils', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('getListeners', () => {
|
||||
it('should return no listeners', () => {
|
||||
expect(getListeners(fixture.hostElement)).toEqual([]);
|
||||
expect(getListeners(child[0])).toEqual([]);
|
||||
});
|
||||
it('should return the listeners', () => {
|
||||
const listeners = getListeners(span[0]);
|
||||
expect(listeners.length).toEqual(1);
|
||||
expect(listeners[0].name).toEqual('click');
|
||||
expect(listeners[0].element).toEqual(span[0]);
|
||||
expect(listeners[0].useCapture).toEqual(false);
|
||||
listeners[0].callback('CLICKED');
|
||||
expect(log).toEqual(['CLICKED']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getInjectionTokens', () => {
|
||||
it('should retrieve tokens', () => {
|
||||
expect(getInjectionTokens(fixture.hostElement)).toEqual([MyApp]);
|
||||
expect(getInjectionTokens(child[0])).toEqual([String, Child]);
|
||||
expect(getInjectionTokens(child[1])).toEqual([String, Child, DirectiveA]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('markDirty', () => {
|
||||
it('should re-render component', () => {
|
||||
expect(span[0].textContent).toEqual('INIT');
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
import {ɵmarkDirty as markDirty} from '@angular/core';
|
||||
|
||||
import {getComponent, getDirectives, getHostElement, getInjector, getRootComponents, getViewComponent} from '../../src/render3/discovery_utils';
|
||||
import {getComponent, getContext, getDirectives, getHostElement, getInjector, getListeners, getRootComponents, getViewComponent} from '../../src/render3/discovery_utils';
|
||||
import {GLOBAL_PUBLISH_EXPANDO_KEY, GlobalDevModeContainer, publishDefaultGlobalUtils, publishGlobalUtil} from '../../src/render3/global_utils';
|
||||
import {getPlayers} from '../../src/render3/players';
|
||||
import {global} from '../../src/util';
|
||||
|
@ -28,6 +28,10 @@ describe('global utils', () => {
|
|||
|
||||
it('should publish getComponent', () => { assertPublished('getComponent', getComponent); });
|
||||
|
||||
it('should publish getContext', () => { assertPublished('getContext', getContext); });
|
||||
|
||||
it('should publish getListeners', () => { assertPublished('getListeners', getListeners); });
|
||||
|
||||
it('should publish getViewComponent',
|
||||
() => { assertPublished('getViewComponent', getViewComponent); });
|
||||
|
||||
|
|
|
@ -659,7 +659,7 @@ describe('host bindings', () => {
|
|||
selectors: [['', 'hostDir', '']],
|
||||
factory: () => new HostBindingDir(),
|
||||
hostBindings: (rf: RenderFlags, ctx: HostBindingDir, elIndex: number) => {
|
||||
// LViewData [..., title, ctx.title, pf1]
|
||||
// LView [..., title, ctx.title, pf1]
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(3);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import {Sanitizer, SecurityContext} from '../../src/sanitization/security';
|
|||
|
||||
import {NgIf} from './common_with_def';
|
||||
import {ComponentFixture, TemplateFixture, createComponent, renderToHtml} from './render_util';
|
||||
import {getContext} from '../../src/render3/context_discovery';
|
||||
import {getLContext} from '../../src/render3/context_discovery';
|
||||
import {StylingIndex} from '../../src/render3/interfaces/styling';
|
||||
import {MONKEY_PATCH_KEY_NAME} from '../../src/render3/interfaces/context';
|
||||
|
||||
|
@ -2041,14 +2041,14 @@ describe('render3 integration test', () => {
|
|||
fixture.update();
|
||||
|
||||
const section = fixture.hostElement.querySelector('section') !;
|
||||
const sectionContext = getContext(section) !;
|
||||
const sectionContext = getLContext(section) !;
|
||||
const sectionLView = sectionContext.lView !;
|
||||
expect(sectionContext.nodeIndex).toEqual(HEADER_OFFSET);
|
||||
expect(sectionLView.length).toBeGreaterThan(HEADER_OFFSET);
|
||||
expect(sectionContext.native).toBe(section);
|
||||
|
||||
const div = fixture.hostElement.querySelector('div') !;
|
||||
const divContext = getContext(div) !;
|
||||
const divContext = getLContext(div) !;
|
||||
const divLView = divContext.lView !;
|
||||
expect(divContext.nodeIndex).toEqual(HEADER_OFFSET + 1);
|
||||
expect(divLView.length).toBeGreaterThan(HEADER_OFFSET);
|
||||
|
@ -2080,7 +2080,7 @@ describe('render3 integration test', () => {
|
|||
const result1 = section[MONKEY_PATCH_KEY_NAME];
|
||||
expect(Array.isArray(result1)).toBeTruthy();
|
||||
|
||||
const context = getContext(section) !;
|
||||
const context = getLContext(section) !;
|
||||
const result2 = section[MONKEY_PATCH_KEY_NAME];
|
||||
expect(Array.isArray(result2)).toBeFalsy();
|
||||
|
||||
|
@ -2116,7 +2116,7 @@ describe('render3 integration test', () => {
|
|||
const p = fixture.hostElement.querySelector('p') !as any;
|
||||
expect(p[MONKEY_PATCH_KEY_NAME]).toBeFalsy();
|
||||
|
||||
const pContext = getContext(p) !;
|
||||
const pContext = getLContext(p) !;
|
||||
expect(pContext.native).toBe(p);
|
||||
expect(p[MONKEY_PATCH_KEY_NAME]).toBe(pContext);
|
||||
});
|
||||
|
@ -2154,7 +2154,7 @@ describe('render3 integration test', () => {
|
|||
expect(Array.isArray(elementResult)).toBeTruthy();
|
||||
expect(elementResult[StylingIndex.ElementPosition]).toBe(section);
|
||||
|
||||
const context = getContext(section) !;
|
||||
const context = getLContext(section) !;
|
||||
const result2 = section[MONKEY_PATCH_KEY_NAME];
|
||||
expect(Array.isArray(result2)).toBeFalsy();
|
||||
|
||||
|
@ -2248,9 +2248,9 @@ describe('render3 integration test', () => {
|
|||
expect(pText[MONKEY_PATCH_KEY_NAME]).toBeFalsy();
|
||||
expect(projectedTextNode[MONKEY_PATCH_KEY_NAME]).toBeTruthy();
|
||||
|
||||
const parentContext = getContext(section) !;
|
||||
const shadowContext = getContext(header) !;
|
||||
const projectedContext = getContext(p) !;
|
||||
const parentContext = getLContext(section) !;
|
||||
const shadowContext = getLContext(header) !;
|
||||
const projectedContext = getLContext(p) !;
|
||||
|
||||
const parentComponentData = parentContext.lView;
|
||||
const shadowComponentData = shadowContext.lView;
|
||||
|
@ -2263,12 +2263,12 @@ describe('render3 integration test', () => {
|
|||
it('should return `null` when an element context is retrieved that isn\'t situated in Angular',
|
||||
() => {
|
||||
const elm1 = document.createElement('div');
|
||||
const context1 = getContext(elm1);
|
||||
const context1 = getLContext(elm1);
|
||||
expect(context1).toBeFalsy();
|
||||
|
||||
const elm2 = document.createElement('div');
|
||||
document.body.appendChild(elm2);
|
||||
const context2 = getContext(elm2);
|
||||
const context2 = getLContext(elm2);
|
||||
expect(context2).toBeFalsy();
|
||||
});
|
||||
|
||||
|
@ -2296,7 +2296,7 @@ describe('render3 integration test', () => {
|
|||
const manuallyCreatedElement = document.createElement('div');
|
||||
section.appendChild(manuallyCreatedElement);
|
||||
|
||||
const context = getContext(manuallyCreatedElement);
|
||||
const context = getLContext(manuallyCreatedElement);
|
||||
expect(context).toBeFalsy();
|
||||
});
|
||||
|
||||
|
@ -2324,11 +2324,11 @@ describe('render3 integration test', () => {
|
|||
const hostLView = (hostElm as any)[MONKEY_PATCH_KEY_NAME];
|
||||
expect(hostLView).toBe(componentLView);
|
||||
|
||||
const context1 = getContext(hostElm) !;
|
||||
const context1 = getLContext(hostElm) !;
|
||||
expect(context1.lView).toBe(hostLView);
|
||||
expect(context1.native).toEqual(hostElm);
|
||||
|
||||
const context2 = getContext(component) !;
|
||||
const context2 = getLContext(component) !;
|
||||
expect(context2).toBe(context1);
|
||||
expect(context2.lView).toBe(hostLView);
|
||||
expect(context2.native).toEqual(hostElm);
|
||||
|
@ -2387,7 +2387,7 @@ describe('render3 integration test', () => {
|
|||
const hostElm = fixture.hostElement;
|
||||
const div1 = hostElm.querySelector('div:first-child') !as any;
|
||||
const div2 = hostElm.querySelector('div:last-child') !as any;
|
||||
const context = getContext(hostElm) !;
|
||||
const context = getLContext(hostElm) !;
|
||||
const componentView = context.lView[context.nodeIndex];
|
||||
|
||||
expect(componentView).toContain(myDir1Instance);
|
||||
|
@ -2398,9 +2398,9 @@ describe('render3 integration test', () => {
|
|||
expect(Array.isArray((myDir2Instance as any)[MONKEY_PATCH_KEY_NAME])).toBeTruthy();
|
||||
expect(Array.isArray((myDir3Instance as any)[MONKEY_PATCH_KEY_NAME])).toBeTruthy();
|
||||
|
||||
const d1Context = getContext(myDir1Instance) !;
|
||||
const d2Context = getContext(myDir2Instance) !;
|
||||
const d3Context = getContext(myDir3Instance) !;
|
||||
const d1Context = getLContext(myDir1Instance) !;
|
||||
const d2Context = getLContext(myDir2Instance) !;
|
||||
const d3Context = getLContext(myDir3Instance) !;
|
||||
|
||||
expect(d1Context.lView).toEqual(componentView);
|
||||
expect(d2Context.lView).toEqual(componentView);
|
||||
|
@ -2487,28 +2487,28 @@ describe('render3 integration test', () => {
|
|||
expect((myDir2Instance as any)[MONKEY_PATCH_KEY_NAME]).toBe(lView);
|
||||
expect((childComponentInstance as any)[MONKEY_PATCH_KEY_NAME]).toBe(lView);
|
||||
|
||||
const childNodeContext = getContext(childCompHostElm) !;
|
||||
const childNodeContext = getLContext(childCompHostElm) !;
|
||||
expect(childNodeContext.component).toBeFalsy();
|
||||
expect(childNodeContext.directives).toBeFalsy();
|
||||
assertMonkeyPatchValueIsLView(myDir1Instance);
|
||||
assertMonkeyPatchValueIsLView(myDir2Instance);
|
||||
assertMonkeyPatchValueIsLView(childComponentInstance);
|
||||
|
||||
expect(getContext(myDir1Instance)).toBe(childNodeContext);
|
||||
expect(getLContext(myDir1Instance)).toBe(childNodeContext);
|
||||
expect(childNodeContext.component).toBeFalsy();
|
||||
expect(childNodeContext.directives !.length).toEqual(2);
|
||||
assertMonkeyPatchValueIsLView(myDir1Instance, false);
|
||||
assertMonkeyPatchValueIsLView(myDir2Instance, false);
|
||||
assertMonkeyPatchValueIsLView(childComponentInstance);
|
||||
|
||||
expect(getContext(myDir2Instance)).toBe(childNodeContext);
|
||||
expect(getLContext(myDir2Instance)).toBe(childNodeContext);
|
||||
expect(childNodeContext.component).toBeFalsy();
|
||||
expect(childNodeContext.directives !.length).toEqual(2);
|
||||
assertMonkeyPatchValueIsLView(myDir1Instance, false);
|
||||
assertMonkeyPatchValueIsLView(myDir2Instance, false);
|
||||
assertMonkeyPatchValueIsLView(childComponentInstance);
|
||||
|
||||
expect(getContext(childComponentInstance)).toBe(childNodeContext);
|
||||
expect(getLContext(childComponentInstance)).toBe(childNodeContext);
|
||||
expect(childNodeContext.component).toBeTruthy();
|
||||
expect(childNodeContext.directives !.length).toEqual(2);
|
||||
assertMonkeyPatchValueIsLView(myDir1Instance, false);
|
||||
|
@ -2565,7 +2565,7 @@ describe('render3 integration test', () => {
|
|||
const child = host.querySelector('child-comp') as any;
|
||||
expect(child[MONKEY_PATCH_KEY_NAME]).toBeTruthy();
|
||||
|
||||
const context = getContext(child) !;
|
||||
const context = getLContext(child) !;
|
||||
expect(child[MONKEY_PATCH_KEY_NAME]).toBeTruthy();
|
||||
|
||||
const componentData = context.lView[context.nodeIndex];
|
||||
|
@ -2573,7 +2573,7 @@ describe('render3 integration test', () => {
|
|||
expect(component instanceof ChildComp).toBeTruthy();
|
||||
expect(component[MONKEY_PATCH_KEY_NAME]).toBe(context.lView);
|
||||
|
||||
const componentContext = getContext(component) !;
|
||||
const componentContext = getLContext(component) !;
|
||||
expect(component[MONKEY_PATCH_KEY_NAME]).toBe(componentContext);
|
||||
expect(componentContext.nodeIndex).toEqual(context.nodeIndex);
|
||||
expect(componentContext.native).toEqual(context.native);
|
||||
|
|
|
@ -21,7 +21,7 @@ import {SWITCH_TEMPLATE_REF_FACTORY__POST_R3__ as R3_TEMPLATE_REF_FACTORY} from
|
|||
import {SWITCH_VIEW_CONTAINER_REF_FACTORY__POST_R3__ as R3_VIEW_CONTAINER_REF_FACTORY} from '../../src/linker/view_container_ref';
|
||||
import {SWITCH_RENDERER2_FACTORY__POST_R3__ as R3_RENDERER2_FACTORY} from '../../src/render/api';
|
||||
import {CreateComponentOptions} from '../../src/render3/component';
|
||||
import {getContext, getDirectivesAtNodeIndex, isComponentInstance} from '../../src/render3/context_discovery';
|
||||
import {getDirectivesAtNodeIndex, getLContext, isComponentInstance} from '../../src/render3/context_discovery';
|
||||
import {extractDirectiveDef, extractPipeDef} from '../../src/render3/definition';
|
||||
import {NG_ELEMENT_ID} from '../../src/render3/fields';
|
||||
import {ComponentTemplate, ComponentType, DirectiveDef, DirectiveType, RenderFlags, defineComponent, defineDirective, renderComponent as _renderComponent, tick} from '../../src/render3/index';
|
||||
|
@ -266,7 +266,7 @@ export function renderComponent<T>(type: ComponentType<T>, opts?: CreateComponen
|
|||
export function toHtml<T>(componentOrElement: T | RElement, keepNgReflect = false): string {
|
||||
let element: any;
|
||||
if (isComponentInstance(componentOrElement)) {
|
||||
const context = getContext(componentOrElement);
|
||||
const context = getLContext(componentOrElement);
|
||||
element = context ? context.native : null;
|
||||
} else {
|
||||
element = componentOrElement;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {createRootContext} from '../../../src/render3/component';
|
||||
import {getContext} from '../../../src/render3/context_discovery';
|
||||
import {getLContext} from '../../../src/render3/context_discovery';
|
||||
import {defineComponent} from '../../../src/render3/index';
|
||||
import {createLView, createTView, elementClassProp, elementEnd, elementStart, elementStyleProp, elementStyling, elementStylingApply, elementStylingMap} from '../../../src/render3/instructions';
|
||||
import {InitialStylingFlags, RenderFlags} from '../../../src/render3/interfaces/definition';
|
||||
|
@ -2264,7 +2264,7 @@ describe('style and class based bindings', () => {
|
|||
fixture.update();
|
||||
|
||||
const target = fixture.hostElement.querySelector('div') !as any;
|
||||
const elementContext = getContext(target) !;
|
||||
const elementContext = getLContext(target) !;
|
||||
const context = elementContext.lView[elementContext.nodeIndex] as StylingContext;
|
||||
|
||||
expect(players.length).toEqual(4);
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
export declare function getComponent<T = {}>(element: Element): T | null;
|
||||
|
||||
export declare function getContext<T = {}>(element: Element): T | null;
|
||||
|
||||
export declare function getDirectives(target: {}): Array<{}>;
|
||||
|
||||
export declare function getHostElement<T>(directive: T): Element;
|
||||
|
||||
export declare function getInjector(target: {}): Injector;
|
||||
|
||||
export declare function getListeners(element: Element): Listener[];
|
||||
|
||||
export declare function getPlayers(ref: ComponentInstance | DirectiveInstance | HTMLElement): Player[];
|
||||
|
||||
export declare function getRootComponents(target: {}): any[];
|
||||
|
|
Loading…
Reference in New Issue