fix(ivy): align discovery methods for consistency (#27117)
PR Close #27117
This commit is contained in:
parent
ca40565f9a
commit
e56c8bf8d1
|
@ -6,7 +6,8 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Optional, SkipSelf, StaticProvider} from '../../di';
|
import {Optional, SkipSelf, StaticProvider, defineInjectable} from '../../di';
|
||||||
|
import {DefaultKeyValueDifferFactory} from './default_keyvalue_differ';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -116,6 +117,12 @@ export interface KeyValueDifferFactory {
|
||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
export class KeyValueDiffers {
|
export class KeyValueDiffers {
|
||||||
|
/** @nocollapse */
|
||||||
|
static ngInjectableDef = defineInjectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
factory: () => new KeyValueDiffers([new DefaultKeyValueDifferFactory()])
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated v4.0.0 - Should be private.
|
* @deprecated v4.0.0 - Should be private.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -200,6 +200,7 @@ export class R3Injector {
|
||||||
defOrWrappedDef: InjectorType<any>|InjectorTypeWithProviders<any>,
|
defOrWrappedDef: InjectorType<any>|InjectorTypeWithProviders<any>,
|
||||||
parents: InjectorType<any>[], dedupStack: InjectorType<any>[]) {
|
parents: InjectorType<any>[], dedupStack: InjectorType<any>[]) {
|
||||||
defOrWrappedDef = resolveForwardRef(defOrWrappedDef);
|
defOrWrappedDef = resolveForwardRef(defOrWrappedDef);
|
||||||
|
if (!defOrWrappedDef) return;
|
||||||
|
|
||||||
// Either the defOrWrappedDef is an InjectorType (with ngInjectorDef) or an
|
// Either the defOrWrappedDef is an InjectorType (with ngInjectorDef) or an
|
||||||
// InjectorDefTypeWithProviders (aka ModuleWithProviders). Detecting either is a megamorphic
|
// InjectorDefTypeWithProviders (aka ModuleWithProviders). Detecting either is a megamorphic
|
||||||
|
|
|
@ -13,9 +13,10 @@ import {Injector} from '../di/injector';
|
||||||
import {Sanitizer} from '../sanitization/security';
|
import {Sanitizer} from '../sanitization/security';
|
||||||
|
|
||||||
import {assertComponentType, assertDefined} from './assert';
|
import {assertComponentType, assertDefined} from './assert';
|
||||||
import {getComponentViewByInstance} from './context_discovery';
|
import {getContext} from './context_discovery';
|
||||||
import {getComponentDef} from './definition';
|
import {getComponentDef} from './definition';
|
||||||
import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di';
|
import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di';
|
||||||
|
import {getHostElement} from './discovery_utils';
|
||||||
import {publishDefaultGlobalUtils} from './global_utils';
|
import {publishDefaultGlobalUtils} from './global_utils';
|
||||||
import {queueInitHooks, queueLifecycleHooks} from './hooks';
|
import {queueInitHooks, queueLifecycleHooks} from './hooks';
|
||||||
import {CLEAN_PROMISE, createLViewData, createNodeAtIndex, createTView, getOrCreateTView, initNodeFlags, instantiateRootComponent, locateHostElement, prefillHostVars, queueComponentIndexForCheck, refreshDescendantViews} from './instructions';
|
import {CLEAN_PROMISE, createLViewData, createNodeAtIndex, createTView, getOrCreateTView, initNodeFlags, instantiateRootComponent, locateHostElement, prefillHostVars, queueComponentIndexForCheck, refreshDescendantViews} from './instructions';
|
||||||
|
@ -28,6 +29,7 @@ import {enterView, leaveView, resetComponentState} from './state';
|
||||||
import {defaultScheduler, getRootView, readElementValue, readPatchedLViewData, stringify} from './util';
|
import {defaultScheduler, getRootView, readElementValue, readPatchedLViewData, stringify} from './util';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Options that control how the component should be bootstrapped. */
|
/** Options that control how the component should be bootstrapped. */
|
||||||
export interface CreateComponentOptions {
|
export interface CreateComponentOptions {
|
||||||
/** Which renderer factory to use. */
|
/** Which renderer factory to use. */
|
||||||
|
@ -121,8 +123,8 @@ export function renderComponent<T>(
|
||||||
|
|
||||||
const renderer = rendererFactory.createRenderer(hostRNode, componentDef);
|
const renderer = rendererFactory.createRenderer(hostRNode, componentDef);
|
||||||
const rootView: LViewData = createLViewData(
|
const rootView: LViewData = createLViewData(
|
||||||
renderer, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags, undefined,
|
null, renderer, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags,
|
||||||
opts.injector || null);
|
undefined, opts.injector || null);
|
||||||
|
|
||||||
const oldView = enterView(rootView, null);
|
const oldView = enterView(rootView, null);
|
||||||
let component: T;
|
let component: T;
|
||||||
|
@ -159,7 +161,7 @@ export function createRootComponentView(
|
||||||
resetComponentState();
|
resetComponentState();
|
||||||
const tView = rootView[TVIEW];
|
const tView = rootView[TVIEW];
|
||||||
const componentView = createLViewData(
|
const componentView = createLViewData(
|
||||||
renderer,
|
rootView, renderer,
|
||||||
getOrCreateTView(
|
getOrCreateTView(
|
||||||
def.template, def.consts, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery),
|
def.template, def.consts, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery),
|
||||||
null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, sanitizer);
|
null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, sanitizer);
|
||||||
|
@ -243,32 +245,6 @@ function getRootContext(component: any): RootContext {
|
||||||
return rootContext;
|
return rootContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the host element of the component.
|
|
||||||
*
|
|
||||||
* Use this function to retrieve the host element of the component. The host
|
|
||||||
* element is the element which the component is associated with.
|
|
||||||
*
|
|
||||||
* @param component Component for which the host element should be retrieved.
|
|
||||||
*/
|
|
||||||
export function getHostElement<T>(component: T): HTMLElement {
|
|
||||||
return readElementValue(getComponentViewByInstance(component)) as HTMLElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the rendered text for a given component.
|
|
||||||
*
|
|
||||||
* This function retrieves the host element of a component and
|
|
||||||
* and then returns the `textContent` for that element. This implies
|
|
||||||
* that the text returned will include re-projected content of
|
|
||||||
* the component as well.
|
|
||||||
*
|
|
||||||
* @param component The component to return the content text for.
|
|
||||||
*/
|
|
||||||
export function getRenderedText(component: any): string {
|
|
||||||
const hostElement = getHostElement(component);
|
|
||||||
return hostElement.textContent || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait on component until it is rendered.
|
* Wait on component until it is rendered.
|
||||||
|
|
|
@ -151,8 +151,8 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
|
||||||
|
|
||||||
// Create the root view. Uses empty TView and ContentTemplate.
|
// Create the root view. Uses empty TView and ContentTemplate.
|
||||||
const rootView: LViewData = createLViewData(
|
const rootView: LViewData = createLViewData(
|
||||||
renderer, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags, undefined,
|
null, renderer, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags,
|
||||||
rootViewInjector);
|
undefined, rootViewInjector);
|
||||||
|
|
||||||
// rootView is the parent when bootstrapping
|
// rootView is the parent when bootstrapping
|
||||||
const oldView = enterView(rootView, null);
|
const oldView = enterView(rootView, null);
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
import './ng_dev_mode';
|
import './ng_dev_mode';
|
||||||
|
|
||||||
import {assertEqual} from './assert';
|
import {assertEqual} from './assert';
|
||||||
|
import {EMPTY_ARRAY} from './definition';
|
||||||
import {LContext, MONKEY_PATCH_KEY_NAME} from './interfaces/context';
|
import {LContext, MONKEY_PATCH_KEY_NAME} from './interfaces/context';
|
||||||
import {TNode, TNodeFlags} from './interfaces/node';
|
import {TNode, TNodeFlags} from './interfaces/node';
|
||||||
import {RElement} from './interfaces/renderer';
|
import {RElement} from './interfaces/renderer';
|
||||||
|
@ -15,6 +16,7 @@ import {CONTEXT, HEADER_OFFSET, HOST, LViewData, TVIEW} from './interfaces/view'
|
||||||
import {getComponentViewByIndex, getNativeByTNode, readElementValue, readPatchedData} from './util';
|
import {getComponentViewByIndex, getNativeByTNode, readElementValue, readPatchedData} from './util';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Returns the matching `LContext` data for a given DOM node, directive or component instance.
|
/** Returns the matching `LContext` data for a given DOM node, directive or component instance.
|
||||||
*
|
*
|
||||||
* This function will examine the provided DOM element, component, or directive instance\'s
|
* This function will examine the provided DOM element, component, or directive instance\'s
|
||||||
|
@ -31,6 +33,8 @@ import {getComponentViewByIndex, getNativeByTNode, readElementValue, readPatched
|
||||||
* will be updated with a new context (which is then returned). If the monkey-patch value is not
|
* will be updated with a new context (which is then returned). If the monkey-patch value is not
|
||||||
* detected for a component/directive instance then it will throw an error (all components and
|
* detected for a component/directive instance then it will throw an error (all components and
|
||||||
* directives should be automatically monkey-patched by ivy).
|
* directives should be automatically monkey-patched by ivy).
|
||||||
|
*
|
||||||
|
* @param target Component, Directive or DOM Node.
|
||||||
*/
|
*/
|
||||||
export function getContext(target: any): LContext|null {
|
export function getContext(target: any): LContext|null {
|
||||||
let mpValue = readPatchedData(target);
|
let mpValue = readPatchedData(target);
|
||||||
|
@ -54,7 +58,7 @@ export function getContext(target: any): LContext|null {
|
||||||
if (nodeIndex == -1) {
|
if (nodeIndex == -1) {
|
||||||
throw new Error('The provided directive was not found in the application');
|
throw new Error('The provided directive was not found in the application');
|
||||||
}
|
}
|
||||||
directives = discoverDirectives(nodeIndex, lViewData, false);
|
directives = getDirectivesAtNodeIndex(nodeIndex, lViewData, false);
|
||||||
} else {
|
} else {
|
||||||
nodeIndex = findViaNativeElement(lViewData, target as RElement);
|
nodeIndex = findViaNativeElement(lViewData, target as RElement);
|
||||||
if (nodeIndex == -1) {
|
if (nodeIndex == -1) {
|
||||||
|
@ -132,7 +136,8 @@ export function getContext(target: any): LContext|null {
|
||||||
function createLContext(lViewData: LViewData, nodeIndex: number, native: RElement): LContext {
|
function createLContext(lViewData: LViewData, nodeIndex: number, native: RElement): LContext {
|
||||||
return {
|
return {
|
||||||
lViewData,
|
lViewData,
|
||||||
nodeIndex: nodeIndex, native,
|
nodeIndex,
|
||||||
|
native,
|
||||||
component: undefined,
|
component: undefined,
|
||||||
directives: undefined,
|
directives: undefined,
|
||||||
localRefs: undefined,
|
localRefs: undefined,
|
||||||
|
@ -271,15 +276,22 @@ function assertDomElement(element: any) {
|
||||||
* @param lViewData The target view data
|
* @param lViewData The target view data
|
||||||
* @param includeComponents Whether or not to include components in returned directives
|
* @param includeComponents Whether or not to include components in returned directives
|
||||||
*/
|
*/
|
||||||
export function discoverDirectives(
|
export function getDirectivesAtNodeIndex(
|
||||||
nodeIndex: number, lViewData: LViewData, includeComponents: boolean): any[]|null {
|
nodeIndex: number, lViewData: LViewData, includeComponents: boolean): any[]|null {
|
||||||
const tNode = lViewData[TVIEW].data[nodeIndex] as TNode;
|
const tNode = lViewData[TVIEW].data[nodeIndex] as TNode;
|
||||||
let directiveStartIndex = getDirectiveStartIndex(tNode);
|
let directiveStartIndex = getDirectiveStartIndex(tNode);
|
||||||
|
if (directiveStartIndex == 0) return EMPTY_ARRAY;
|
||||||
const directiveEndIndex = getDirectiveEndIndex(tNode, directiveStartIndex);
|
const directiveEndIndex = getDirectiveEndIndex(tNode, directiveStartIndex);
|
||||||
if (!includeComponents && tNode.flags & TNodeFlags.isComponent) directiveStartIndex++;
|
if (!includeComponents && tNode.flags & TNodeFlags.isComponent) directiveStartIndex++;
|
||||||
return lViewData.slice(directiveStartIndex, directiveEndIndex);
|
return lViewData.slice(directiveStartIndex, directiveEndIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getComponentAtNodeIndex(nodeIndex: number, lViewData: LViewData): {}|null {
|
||||||
|
const tNode = lViewData[TVIEW].data[nodeIndex] as TNode;
|
||||||
|
let directiveStartIndex = getDirectiveStartIndex(tNode);
|
||||||
|
return tNode.flags & TNodeFlags.isComponent ? lViewData[directiveStartIndex] : null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a map of local references (local reference name => element or directive instance) that
|
* Returns a map of local references (local reference name => element or directive instance) that
|
||||||
* exist on a given element.
|
* exist on a given element.
|
||||||
|
|
|
@ -11,11 +11,12 @@ import {Renderer2, RendererType2} from '../render/api';
|
||||||
import {DebugContext} from '../view';
|
import {DebugContext} from '../view';
|
||||||
import {DebugRenderer2, DebugRendererFactory2} from '../view/services';
|
import {DebugRenderer2, DebugRendererFactory2} from '../view/services';
|
||||||
|
|
||||||
import {getHostComponent, getInjector, getLocalRefs, loadContext} from './discovery_utils';
|
import {getComponent, getInjector, getLocalRefs, loadContext} from './discovery_utils';
|
||||||
import {DirectiveDef} from './interfaces/definition';
|
import {DirectiveDef} from './interfaces/definition';
|
||||||
import {TNode, TNodeFlags} from './interfaces/node';
|
import {TNode, TNodeFlags} from './interfaces/node';
|
||||||
import {TVIEW} from './interfaces/view';
|
import {TVIEW} from './interfaces/view';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapts the DebugRendererFactory2 to create a DebugRenderer2 specific for IVY.
|
* Adapts the DebugRendererFactory2 to create a DebugRenderer2 specific for IVY.
|
||||||
*
|
*
|
||||||
|
@ -43,7 +44,7 @@ class Render3DebugContext implements DebugContext {
|
||||||
|
|
||||||
get injector(): Injector { return getInjector(this._nativeNode); }
|
get injector(): Injector { return getInjector(this._nativeNode); }
|
||||||
|
|
||||||
get component(): any { return getHostComponent(this._nativeNode); }
|
get component(): any { return getComponent(this._nativeNode); }
|
||||||
|
|
||||||
get providerTokens(): any[] {
|
get providerTokens(): any[] {
|
||||||
const lDebugCtx = loadContext(this._nativeNode);
|
const lDebugCtx = loadContext(this._nativeNode);
|
||||||
|
|
|
@ -8,78 +8,83 @@
|
||||||
import {Injector} from '../di/injector';
|
import {Injector} from '../di/injector';
|
||||||
|
|
||||||
import {assertDefined} from './assert';
|
import {assertDefined} from './assert';
|
||||||
import {discoverDirectives, discoverLocalRefs, getContext, isComponentInstance} from './context_discovery';
|
import {discoverLocalRefs, getComponentAtNodeIndex, getContext, getDirectivesAtNodeIndex} from './context_discovery';
|
||||||
import {LContext} from './interfaces/context';
|
import {LContext} from './interfaces/context';
|
||||||
import {TElementNode, TNode, TNodeFlags} from './interfaces/node';
|
import {TElementNode} from './interfaces/node';
|
||||||
import {CONTEXT, FLAGS, LViewData, LViewFlags, PARENT, RootContext, TVIEW} from './interfaces/view';
|
import {CONTEXT, FLAGS, HOST, LViewData, LViewFlags, PARENT, RootContext, TVIEW} from './interfaces/view';
|
||||||
import {getComponentViewByIndex, readPatchedLViewData} from './util';
|
import {readPatchedLViewData, stringify} from './util';
|
||||||
import {NodeInjector} from './view_engine_compatibility';
|
import {NodeInjector} from './view_engine_compatibility';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NOTE: The following functions might not be ideal for core usage in Angular...
|
* Returns the component instance associated with a given DOM host element.
|
||||||
|
* Elements which don't represent components return `null`.
|
||||||
*
|
*
|
||||||
* Each function below is designed
|
* @param element Host DOM element from which the component should be retrieved for.
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the component instance associated with the target.
|
|
||||||
*
|
*
|
||||||
* If a DOM is used then it will return the component that
|
* ```
|
||||||
* owns the view where the element is situated.
|
* <my-app>
|
||||||
* If a component instance is used then it will return the
|
* #VIEW
|
||||||
* instance of the parent component depending on where
|
* <div>
|
||||||
* the component instance is exists in a template.
|
* <child-comp></child-comp>
|
||||||
* If a directive instance is used then it will return the
|
* </div>
|
||||||
* component that contains that directive in it's template.
|
* </mp-app>
|
||||||
|
*
|
||||||
|
* expect(getComponent(<child-comp>) instanceof ChildComponent).toBeTruthy();
|
||||||
|
* expect(getComponent(<my-app>) instanceof MyApp).toBeTruthy();
|
||||||
|
* ```
|
||||||
*
|
*
|
||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
export function getComponent<T = {}>(target: {}): T|null {
|
export function getComponent<T = {}>(element: Element): T|null {
|
||||||
const context = loadContext(target) !;
|
if (!(element instanceof Node)) throw new Error('Expecting instance of DOM Node');
|
||||||
|
|
||||||
|
const context = loadContext(element) !;
|
||||||
|
|
||||||
if (context.component === undefined) {
|
if (context.component === undefined) {
|
||||||
let lViewData: LViewData|null = context.lViewData;
|
context.component = getComponentAtNodeIndex(context.nodeIndex, context.lViewData);
|
||||||
while (lViewData) {
|
|
||||||
const ctx = lViewData ![CONTEXT] !as{};
|
|
||||||
if (ctx && isComponentInstance(ctx)) {
|
|
||||||
context.component = ctx;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
lViewData = lViewData[FLAGS] & LViewFlags.IsRoot ? null : lViewData ![PARENT] !;
|
|
||||||
}
|
|
||||||
if (context.component === undefined) {
|
|
||||||
context.component = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return context.component as T;
|
return context.component as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the host component instance associated with the target.
|
* Returns the component instance associated with view which owns the DOM element (`null`
|
||||||
|
* otherwise).
|
||||||
*
|
*
|
||||||
* This will only return a component instance of the DOM node
|
* @param element DOM element which is owned by an existing component's view.
|
||||||
* contains an instance of a component on it.
|
*
|
||||||
|
* ```
|
||||||
|
* <my-app>
|
||||||
|
* #VIEW
|
||||||
|
* <div>
|
||||||
|
* <child-comp></child-comp>
|
||||||
|
* </div>
|
||||||
|
* </mp-app>
|
||||||
|
*
|
||||||
|
* expect(getViewComponent(<child-comp>) instanceof MyApp).toBeTruthy();
|
||||||
|
* expect(getViewComponent(<my-app>)).toEqual(null);
|
||||||
|
* ```
|
||||||
*
|
*
|
||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
export function getHostComponent<T = {}>(target: {}): T|null {
|
export function getViewComponent<T = {}>(element: Element | {}): T|null {
|
||||||
const context = loadContext(target);
|
const context = loadContext(element) !;
|
||||||
const tNode = context.lViewData[TVIEW].data[context.nodeIndex] as TNode;
|
let lView: LViewData = context.lViewData;
|
||||||
if (tNode.flags & TNodeFlags.isComponent) {
|
while (lView[PARENT] && lView[HOST] === null) {
|
||||||
const componentView = getComponentViewByIndex(context.nodeIndex, context.lViewData);
|
// As long as lView[HOST] is null we know we are part of sub-template such as `*ngIf`
|
||||||
return componentView[CONTEXT] as any as T;
|
lView = lView[PARENT] !;
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
return lView[FLAGS] & LViewFlags.IsRoot ? null : lView[CONTEXT] as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the `RootContext` instance that is associated with
|
* Returns the `RootContext` instance that is associated with
|
||||||
* the application where the target is situated.
|
* the application where the target is situated.
|
||||||
*
|
*
|
||||||
* @publicApi
|
|
||||||
*/
|
*/
|
||||||
export function getRootContext(target: LViewData | {}): RootContext {
|
export function getRootContext(target: LViewData | {}): RootContext {
|
||||||
const lViewData = Array.isArray(target) ? target : loadContext(target) !.lViewData;
|
const lViewData = Array.isArray(target) ? target : loadContext(target) !.lViewData;
|
||||||
|
@ -88,8 +93,11 @@ export function getRootContext(target: LViewData | {}): RootContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of all the components in the application
|
* Retrieve all root components.
|
||||||
* that are have been bootstrapped.
|
*
|
||||||
|
* Root components are those which have been bootstrapped by Angular.
|
||||||
|
*
|
||||||
|
* @param target A DOM element, component or directive instance.
|
||||||
*
|
*
|
||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
|
@ -98,8 +106,9 @@ export function getRootComponents(target: {}): any[] {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the injector instance that is associated with
|
* Retrieves an `Injector` associated with the element, component or directive.
|
||||||
* the element, component or directive.
|
*
|
||||||
|
* @param target A DOM element, component or directive instance.
|
||||||
*
|
*
|
||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
|
@ -111,8 +120,9 @@ export function getInjector(target: {}): Injector {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of all the directives that are associated
|
* Retrieves directives associated with a given DOM host element.
|
||||||
* with the underlying target element.
|
*
|
||||||
|
* @param target A DOM element, component or directive instance.
|
||||||
*
|
*
|
||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
|
@ -120,7 +130,7 @@ export function getDirectives(target: {}): Array<{}> {
|
||||||
const context = loadContext(target) !;
|
const context = loadContext(target) !;
|
||||||
|
|
||||||
if (context.directives === undefined) {
|
if (context.directives === undefined) {
|
||||||
context.directives = discoverDirectives(context.nodeIndex, context.lViewData, false);
|
context.directives = getDirectivesAtNodeIndex(context.nodeIndex, context.lViewData, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return context.directives || [];
|
return context.directives || [];
|
||||||
|
@ -130,13 +140,12 @@ export function getDirectives(target: {}): Array<{}> {
|
||||||
* Returns LContext associated with a target passed as an argument.
|
* Returns LContext associated with a target passed as an argument.
|
||||||
* Throws if a given target doesn't have associated LContext.
|
* Throws if a given target doesn't have associated LContext.
|
||||||
*
|
*
|
||||||
* @publicApi
|
|
||||||
*/
|
*/
|
||||||
export function loadContext(target: {}): LContext {
|
export function loadContext(target: {}): LContext {
|
||||||
const context = getContext(target);
|
const context = getContext(target);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
ngDevMode ? 'Unable to find the given context data for the given target' :
|
ngDevMode ? `Unable to find context associated with ${stringify(target)}` :
|
||||||
'Invalid ng target');
|
'Invalid ng target');
|
||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
|
@ -148,7 +157,6 @@ export function loadContext(target: {}): LContext {
|
||||||
*
|
*
|
||||||
* @param componentOrView any component or view
|
* @param componentOrView any component or view
|
||||||
*
|
*
|
||||||
* @publicApi
|
|
||||||
*/
|
*/
|
||||||
export function getRootView(componentOrView: LViewData | {}): LViewData {
|
export function getRootView(componentOrView: LViewData | {}): LViewData {
|
||||||
let lViewData: LViewData;
|
let lViewData: LViewData;
|
||||||
|
@ -166,7 +174,11 @@ export function getRootView(componentOrView: LViewData | {}): LViewData {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve map of local references (local reference name => element or directive instance).
|
* Retrieve map of local references.
|
||||||
|
*
|
||||||
|
* The references are retrieved as a map of local reference name to element or directive instance.
|
||||||
|
*
|
||||||
|
* @param target A DOM element, component or directive instance.
|
||||||
*
|
*
|
||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
|
@ -179,3 +191,32 @@ export function getLocalRefs(target: {}): {[key: string]: any} {
|
||||||
|
|
||||||
return context.localRefs || {};
|
return context.localRefs || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the host element of the component.
|
||||||
|
*
|
||||||
|
* Use this function to retrieve the host element of the component. The host
|
||||||
|
* element is the element which the component is associated with.
|
||||||
|
*
|
||||||
|
* @param directive Component or Directive for which the host element should be retrieved.
|
||||||
|
*
|
||||||
|
* @publicApi
|
||||||
|
*/
|
||||||
|
export function getHostElement<T>(directive: T): Element {
|
||||||
|
return getContext(directive) !.native as never as Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the rendered text for a given component.
|
||||||
|
*
|
||||||
|
* This function retrieves the host element of a component and
|
||||||
|
* and then returns the `textContent` for that element. This implies
|
||||||
|
* that the text returned will include re-projected content of
|
||||||
|
* the component as well.
|
||||||
|
*
|
||||||
|
* @param component The component to return the content text for.
|
||||||
|
*/
|
||||||
|
export function getRenderedText(component: any): string {
|
||||||
|
const hostElement = getHostElement(component);
|
||||||
|
return hostElement.textContent || '';
|
||||||
|
}
|
|
@ -6,7 +6,11 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {global} from '../util';
|
import {global} from '../util';
|
||||||
import {getComponent, getDirectives, getHostComponent, getInjector, getPlayers, getRootComponents} from './global_utils_api';
|
|
||||||
|
import {assertDefined} from './assert';
|
||||||
|
import {getComponent, getDirectives, getHostElement, getInjector, getPlayers, getRootComponents, getViewComponent, markDirty} from './global_utils_api';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This file introduces series of globally accessible debug tools
|
* This file introduces series of globally accessible debug tools
|
||||||
|
@ -37,11 +41,13 @@ export function publishDefaultGlobalUtils() {
|
||||||
if (!_published) {
|
if (!_published) {
|
||||||
_published = true;
|
_published = true;
|
||||||
publishGlobalUtil('getComponent', getComponent);
|
publishGlobalUtil('getComponent', getComponent);
|
||||||
publishGlobalUtil('getHostComponent', getHostComponent);
|
publishGlobalUtil('getViewComponent', getViewComponent);
|
||||||
|
publishGlobalUtil('getHostElement', getHostElement);
|
||||||
publishGlobalUtil('getInjector', getInjector);
|
publishGlobalUtil('getInjector', getInjector);
|
||||||
publishGlobalUtil('getRootComponents', getRootComponents);
|
publishGlobalUtil('getRootComponents', getRootComponents);
|
||||||
publishGlobalUtil('getDirectives', getDirectives);
|
publishGlobalUtil('getDirectives', getDirectives);
|
||||||
publishGlobalUtil('getPlayers', getPlayers);
|
publishGlobalUtil('getPlayers', getPlayers);
|
||||||
|
publishGlobalUtil('markDirty', markDirty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +61,7 @@ export declare type GlobalDevModeContainer = {
|
||||||
*/
|
*/
|
||||||
export function publishGlobalUtil(name: string, fn: Function): void {
|
export function publishGlobalUtil(name: string, fn: Function): void {
|
||||||
const w = global as any as GlobalDevModeContainer;
|
const w = global as any as GlobalDevModeContainer;
|
||||||
|
ngDevMode && assertDefined(fn, 'function not defined');
|
||||||
if (w) {
|
if (w) {
|
||||||
let container = w[GLOBAL_PUBLISH_EXPANDO_KEY];
|
let container = w[GLOBAL_PUBLISH_EXPANDO_KEY];
|
||||||
if (!container) {
|
if (!container) {
|
||||||
|
|
|
@ -15,5 +15,6 @@
|
||||||
* file in the public_api_guard test.
|
* file in the public_api_guard test.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export {getComponent, getDirectives, getHostComponent, getInjector, getRootComponents} from './discovery_utils';
|
export {getComponent, getDirectives, getHostElement, getInjector, getRootComponents, getViewComponent} from './discovery_utils';
|
||||||
|
export {markDirty} from './instructions';
|
||||||
export {getPlayers} from './players';
|
export {getPlayers} from './players';
|
||||||
|
|
|
@ -5,8 +5,9 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {LifecycleHooksFeature, getHostElement, getRenderedText, renderComponent, whenRendered} from './component';
|
import {LifecycleHooksFeature, renderComponent, whenRendered} from './component';
|
||||||
import {defineBase, defineComponent, defineDirective, defineNgModule, definePipe} from './definition';
|
import {defineBase, defineComponent, defineDirective, defineNgModule, definePipe} from './definition';
|
||||||
|
import {getHostElement, getRenderedText} from './discovery_utils';
|
||||||
import {InheritDefinitionFeature} from './features/inherit_definition_feature';
|
import {InheritDefinitionFeature} from './features/inherit_definition_feature';
|
||||||
import {NgOnChangesFeature} from './features/ng_onchanges_feature';
|
import {NgOnChangesFeature} from './features/ng_onchanges_feature';
|
||||||
import {ProvidersFeature} from './features/providers_feature';
|
import {ProvidersFeature} from './features/providers_feature';
|
||||||
|
@ -17,6 +18,7 @@ export {getFactoryOf, getInheritedFactory} from './di';
|
||||||
export {RenderFlags} from './interfaces/definition';
|
export {RenderFlags} from './interfaces/definition';
|
||||||
export {CssSelectorList} from './interfaces/projection';
|
export {CssSelectorList} from './interfaces/projection';
|
||||||
|
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
export {
|
export {
|
||||||
bind,
|
bind,
|
||||||
|
|
|
@ -163,15 +163,14 @@ function refreshChildComponents(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createLViewData<T>(
|
export function createLViewData<T>(
|
||||||
renderer: Renderer3, tView: TView, context: T | null, flags: LViewFlags,
|
parentViewData: LViewData | null, renderer: Renderer3, tView: TView, context: T | null,
|
||||||
sanitizer?: Sanitizer | null, injector?: Injector | null): LViewData {
|
flags: LViewFlags, sanitizer?: Sanitizer | null, injector?: Injector | null): LViewData {
|
||||||
const viewData = getViewData();
|
|
||||||
const instance = tView.blueprint.slice() as LViewData;
|
const instance = tView.blueprint.slice() as LViewData;
|
||||||
instance[FLAGS] = flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.RunInit;
|
instance[FLAGS] = flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.RunInit;
|
||||||
instance[PARENT] = instance[DECLARATION_VIEW] = viewData;
|
instance[PARENT] = instance[DECLARATION_VIEW] = parentViewData;
|
||||||
instance[CONTEXT] = context;
|
instance[CONTEXT] = context;
|
||||||
instance[INJECTOR as any] =
|
instance[INJECTOR as any] =
|
||||||
injector === undefined ? (viewData ? viewData[INJECTOR] : null) : injector;
|
injector === undefined ? (parentViewData ? parentViewData[INJECTOR] : null) : injector;
|
||||||
instance[RENDERER] = renderer;
|
instance[RENDERER] = renderer;
|
||||||
instance[SANITIZER] = sanitizer || null;
|
instance[SANITIZER] = sanitizer || null;
|
||||||
return instance;
|
return instance;
|
||||||
|
@ -299,16 +298,15 @@ export function renderTemplate<T>(
|
||||||
setRenderer(renderer);
|
setRenderer(renderer);
|
||||||
|
|
||||||
// We need to create a root view so it's possible to look up the host element through its index
|
// We need to create a root view so it's possible to look up the host element through its index
|
||||||
enterView(
|
const lView = createLViewData(
|
||||||
createLViewData(
|
null, renderer, createTView(-1, null, 1, 0, null, null, null), {},
|
||||||
renderer, createTView(-1, null, 1, 0, null, null, null), {},
|
LViewFlags.CheckAlways | LViewFlags.IsRoot);
|
||||||
LViewFlags.CheckAlways | LViewFlags.IsRoot),
|
enterView(lView, null);
|
||||||
null);
|
|
||||||
|
|
||||||
const componentTView =
|
const componentTView =
|
||||||
getOrCreateTView(templateFn, consts, vars, directives || null, pipes || null, null);
|
getOrCreateTView(templateFn, consts, vars, directives || null, pipes || null, null);
|
||||||
hostView =
|
hostView = createLViewData(
|
||||||
createLViewData(renderer, componentTView, context, LViewFlags.CheckAlways, sanitizer);
|
lView, renderer, componentTView, context, LViewFlags.CheckAlways, sanitizer);
|
||||||
hostView[HOST_NODE] = createNodeAtIndex(0, TNodeType.Element, hostNode, null, null);
|
hostView[HOST_NODE] = createNodeAtIndex(0, TNodeType.Element, hostNode, null, null);
|
||||||
}
|
}
|
||||||
renderComponentOrTemplate(hostView, context, null, templateFn);
|
renderComponentOrTemplate(hostView, context, null, templateFn);
|
||||||
|
@ -329,8 +327,8 @@ export function createEmbeddedViewAndNode<T>(
|
||||||
setIsParent(true);
|
setIsParent(true);
|
||||||
setPreviousOrParentTNode(null !);
|
setPreviousOrParentTNode(null !);
|
||||||
|
|
||||||
const lView =
|
const lView = createLViewData(
|
||||||
createLViewData(renderer, tView, context, LViewFlags.CheckAlways, getCurrentSanitizer());
|
declarationView, renderer, tView, context, LViewFlags.CheckAlways, getCurrentSanitizer());
|
||||||
lView[DECLARATION_VIEW] = declarationView;
|
lView[DECLARATION_VIEW] = declarationView;
|
||||||
|
|
||||||
if (queries) {
|
if (queries) {
|
||||||
|
@ -1672,7 +1670,7 @@ function addComponentLogic<T>(
|
||||||
const componentView = addToViewTree(
|
const componentView = addToViewTree(
|
||||||
viewData, previousOrParentTNode.index as number,
|
viewData, previousOrParentTNode.index as number,
|
||||||
createLViewData(
|
createLViewData(
|
||||||
getRendererFactory().createRenderer(native as RElement, def), tView, null,
|
getViewData(), getRendererFactory().createRenderer(native as RElement, def), tView, null,
|
||||||
def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, getCurrentSanitizer()));
|
def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, getCurrentSanitizer()));
|
||||||
|
|
||||||
componentView[HOST_NODE] = previousOrParentTNode as TElementNode;
|
componentView[HOST_NODE] = previousOrParentTNode as TElementNode;
|
||||||
|
@ -2000,7 +1998,7 @@ export function embeddedViewStart(viewBlockId: number, consts: number, vars: num
|
||||||
} else {
|
} else {
|
||||||
// When we create a new LView, we always reset the state of the instructions.
|
// When we create a new LView, we always reset the state of the instructions.
|
||||||
viewToRender = createLViewData(
|
viewToRender = createLViewData(
|
||||||
getRenderer(),
|
getViewData(), getRenderer(),
|
||||||
getOrCreateEmbeddedTView(viewBlockId, consts, vars, containerTNode as TContainerNode), null,
|
getOrCreateEmbeddedTView(viewBlockId, consts, vars, containerTNode as TContainerNode), null,
|
||||||
LViewFlags.CheckAlways, getCurrentSanitizer());
|
LViewFlags.CheckAlways, getCurrentSanitizer());
|
||||||
|
|
||||||
|
@ -2469,6 +2467,8 @@ function updateViewQuery<T>(
|
||||||
* can be provided.
|
* can be provided.
|
||||||
*
|
*
|
||||||
* @param component Component to mark as dirty.
|
* @param component Component to mark as dirty.
|
||||||
|
*
|
||||||
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
export function markDirty<T>(component: T) {
|
export function markDirty<T>(component: T) {
|
||||||
ngDevMode && assertDefined(component, 'component');
|
ngDevMode && assertDefined(component, 'component');
|
||||||
|
|
|
@ -485,9 +485,6 @@
|
||||||
{
|
{
|
||||||
"name": "directiveInject"
|
"name": "directiveInject"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "discoverDirectives"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "domRendererFactory3"
|
"name": "domRendererFactory3"
|
||||||
},
|
},
|
||||||
|
@ -623,6 +620,9 @@
|
||||||
{
|
{
|
||||||
"name": "getDirectiveStartIndex"
|
"name": "getDirectiveStartIndex"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getDirectivesAtNodeIndex"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getElementDepthCount"
|
"name": "getElementDepthCount"
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,8 +10,9 @@ import {TemplateRef, ViewContainerRef} from '@angular/core';
|
||||||
import {withBody} from '@angular/private/testing';
|
import {withBody} from '@angular/private/testing';
|
||||||
|
|
||||||
import {ChangeDetectionStrategy, ChangeDetectorRef, DoCheck, RendererType2} from '../../src/core';
|
import {ChangeDetectionStrategy, ChangeDetectorRef, DoCheck, RendererType2} from '../../src/core';
|
||||||
import {getRenderedText, whenRendered} from '../../src/render3/component';
|
import {whenRendered} from '../../src/render3/component';
|
||||||
import {LifecycleHooksFeature, defineComponent, defineDirective, templateRefExtractor} from '../../src/render3/index';
|
import {LifecycleHooksFeature, defineComponent, defineDirective, getRenderedText, templateRefExtractor} from '../../src/render3/index';
|
||||||
|
|
||||||
import {bind, container, containerRefreshEnd, containerRefreshStart, detectChanges, directiveInject, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, listener, markDirty, reference, text, template, textBinding, tick} from '../../src/render3/instructions';
|
import {bind, container, containerRefreshEnd, containerRefreshStart, detectChanges, directiveInject, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, listener, markDirty, reference, text, template, textBinding, tick} from '../../src/render3/instructions';
|
||||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||||
import {RElement, Renderer3, RendererFactory3} from '../../src/render3/interfaces/renderer';
|
import {RElement, Renderer3, RendererFactory3} from '../../src/render3/interfaces/renderer';
|
||||||
|
|
|
@ -7,9 +7,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ViewEncapsulation, createInjector, defineInjectable, defineInjector} from '../../src/core';
|
import {ViewEncapsulation, createInjector, defineInjectable, defineInjector} from '../../src/core';
|
||||||
import {getRenderedText} from '../../src/render3/component';
|
|
||||||
|
|
||||||
import {AttributeMarker, ComponentFactory, LifecycleHooksFeature, defineComponent, directiveInject, markDirty, template} from '../../src/render3/index';
|
import {AttributeMarker, ComponentFactory, LifecycleHooksFeature, defineComponent, directiveInject, markDirty, template, getRenderedText} from '../../src/render3/index';
|
||||||
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, nextContext, text, textBinding, tick} from '../../src/render3/instructions';
|
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, nextContext, text, textBinding, tick} from '../../src/render3/instructions';
|
||||||
import {ComponentDef, RenderFlags} from '../../src/render3/interfaces/definition';
|
import {ComponentDef, RenderFlags} from '../../src/render3/interfaces/definition';
|
||||||
|
|
||||||
|
|
|
@ -2019,7 +2019,8 @@ describe('di', () => {
|
||||||
describe('getOrCreateNodeInjector', () => {
|
describe('getOrCreateNodeInjector', () => {
|
||||||
it('should handle initial undefined state', () => {
|
it('should handle initial undefined state', () => {
|
||||||
const contentView = createLViewData(
|
const contentView = createLViewData(
|
||||||
null !, createTView(-1, null, 1, 0, null, null, null), null, LViewFlags.CheckAlways);
|
null, null !, createTView(-1, null, 1, 0, null, null, null), null,
|
||||||
|
LViewFlags.CheckAlways);
|
||||||
const oldView = enterView(contentView, null);
|
const oldView = enterView(contentView, null);
|
||||||
try {
|
try {
|
||||||
const parentTNode = createNodeAtIndex(0, TNodeType.Element, null, null, null);
|
const parentTNode = createNodeAtIndex(0, TNodeType.Element, null, null, null);
|
||||||
|
|
|
@ -5,59 +5,263 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* 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 {StaticInjector} from '../../src/di/injector';
|
||||||
import {getComponent, getDirectives, getHostComponent, getInjector, getLocalRefs, getRootComponents} from '../../src/render3/discovery_utils';
|
import {getComponent, getDirectives, getInjector, getLocalRefs, getRootComponents, getViewComponent} from '../../src/render3/discovery_utils';
|
||||||
import {RenderFlags, defineComponent, defineDirective} from '../../src/render3/index';
|
import {ProvidersFeature, RenderFlags, defineComponent, defineDirective, getHostElement} from '../../src/render3/index';
|
||||||
import {element, elementEnd, elementStart, elementStyling, elementStylingApply} from '../../src/render3/instructions';
|
|
||||||
|
import {container, element, elementEnd, elementStart, elementStyling, elementStylingApply, template, bind, elementProperty, text, textBinding, markDirty} from '../../src/render3/instructions';
|
||||||
|
|
||||||
import {ComponentFixture} from './render_util';
|
import {ComponentFixture} from './render_util';
|
||||||
|
import {NgIf} from './common_with_def';
|
||||||
|
import {getRootContext} from '@angular/core/src/render3/util';
|
||||||
|
|
||||||
describe('discovery utils', () => {
|
describe('discovery utils', () => {
|
||||||
describe('getComponent()', () => {
|
let fixture: ComponentFixture<MyApp>;
|
||||||
it('should return the component instance for a DOM element', () => {
|
let myApp: MyApp[];
|
||||||
class InnerComp {
|
let dirA: DirectiveA[];
|
||||||
static ngComponentDef = defineComponent({
|
let childComponent: DirectiveA[];
|
||||||
type: InnerComp,
|
let child: NodeListOf<Element>;
|
||||||
selectors: [['inner-comp']],
|
let span: NodeListOf<Element>;
|
||||||
factory: () => new InnerComp(),
|
let div: NodeListOf<Element>;
|
||||||
consts: 1,
|
let p: NodeListOf<Element>;
|
||||||
vars: 0,
|
|
||||||
template: (rf: RenderFlags, ctx: InnerComp) => {
|
beforeEach(() => {
|
||||||
|
myApp = [];
|
||||||
|
dirA = [];
|
||||||
|
childComponent = [];
|
||||||
|
fixture = new ComponentFixture(
|
||||||
|
MyApp, {injector: createInjector(null, null, [{provide: String, useValue: 'Module'}])});
|
||||||
|
child = fixture.hostElement.querySelectorAll('child');
|
||||||
|
span = fixture.hostElement.querySelectorAll('span');
|
||||||
|
div = fixture.hostElement.querySelectorAll('div');
|
||||||
|
p = fixture.hostElement.querySelectorAll('p');
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For all tests assume this set up
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* <my-app>
|
||||||
|
* <#VIEW>
|
||||||
|
* <span>{{text}}</span>
|
||||||
|
* <div dirA #div #foo="dirA"></div>
|
||||||
|
* <child>
|
||||||
|
* <#VIEW>
|
||||||
|
* <p></p>
|
||||||
|
* <VIEW>
|
||||||
|
* </child>
|
||||||
|
* <child dirA #child>
|
||||||
|
* <#VIEW>
|
||||||
|
* <p></p>
|
||||||
|
* <VIEW>
|
||||||
|
* </child>
|
||||||
|
* <child dirA *ngIf="true">
|
||||||
|
* <#VIEW>
|
||||||
|
* <p></p>
|
||||||
|
* <VIEW>
|
||||||
|
* </child>
|
||||||
|
* </#VIEW>
|
||||||
|
* </my-app>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
class Child {
|
||||||
|
constructor() { childComponent.push(this); }
|
||||||
|
|
||||||
|
static ngComponentDef = defineComponent({
|
||||||
|
type: Child,
|
||||||
|
selectors: [['child']],
|
||||||
|
factory: () => new Child(),
|
||||||
|
consts: 1,
|
||||||
|
vars: 0,
|
||||||
|
template: (rf: RenderFlags, ctx: Child) => {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
element(0, 'p');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
features: [ProvidersFeature([{provide: String, useValue: 'Child'}])]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class DirectiveA {
|
||||||
|
constructor() { dirA.push(this); }
|
||||||
|
|
||||||
|
static ngDirectiveDef = defineDirective({
|
||||||
|
type: DirectiveA,
|
||||||
|
selectors: [['', 'dirA', '']],
|
||||||
|
exportAs: 'dirA',
|
||||||
|
factory: () => new DirectiveA(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyApp {
|
||||||
|
text: string = 'INIT';
|
||||||
|
constructor() { myApp.push(this); }
|
||||||
|
|
||||||
|
static ngComponentDef = defineComponent({
|
||||||
|
type: MyApp,
|
||||||
|
selectors: [['my-app']],
|
||||||
|
factory: () => new MyApp(),
|
||||||
|
consts: 9,
|
||||||
|
vars: 1,
|
||||||
|
directives: [Child, DirectiveA, NgIf],
|
||||||
|
template: (rf: RenderFlags, ctx: MyApp) => {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'span');
|
||||||
|
text(1);
|
||||||
|
elementEnd();
|
||||||
|
element(2, 'div', ['dirA', ''], ['div', '', 'foo', 'dirA']);
|
||||||
|
element(5, 'child');
|
||||||
|
element(6, 'child', ['dirA', ''], ['child', '']);
|
||||||
|
template(8, function(rf: RenderFlags, ctx: never) {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
element(0, 'div');
|
element(0, 'child');
|
||||||
}
|
}
|
||||||
}
|
}, 1, 0, null, ['ngIf', '']);
|
||||||
});
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
textBinding(1, bind(ctx.text));
|
||||||
|
elementProperty(8, 'ngIf', bind(true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
class Comp {
|
describe('getComponent', () => {
|
||||||
static ngComponentDef = defineComponent({
|
it('should return null if no component', () => {
|
||||||
type: Comp,
|
expect(getComponent(span[0])).toEqual(null);
|
||||||
selectors: [['comp']],
|
expect(getComponent(div[0])).toEqual(null);
|
||||||
factory: () => new Comp(),
|
expect(getComponent(p[0])).toEqual(null);
|
||||||
consts: 1,
|
});
|
||||||
vars: 0,
|
it('should throw when called on non-element', () => {
|
||||||
template: (rf: RenderFlags, ctx: Comp) => {
|
expect(() => getComponent(dirA[0] as any)).toThrowError(/Expecting instance of DOM Node/);
|
||||||
if (rf & RenderFlags.Create) {
|
expect(() => getComponent(dirA[1] as any)).toThrowError(/Expecting instance of DOM Node/);
|
||||||
element(0, 'inner-comp');
|
});
|
||||||
}
|
it('should return component from element', () => {
|
||||||
},
|
expect(getComponent<MyApp>(fixture.hostElement)).toEqual(myApp[0]);
|
||||||
directives: [InnerComp]
|
expect(getComponent<Child>(child[0])).toEqual(childComponent[0]);
|
||||||
});
|
expect(getComponent<Child>(child[1])).toEqual(childComponent[1]);
|
||||||
}
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(Comp);
|
|
||||||
fixture.update();
|
|
||||||
|
|
||||||
const hostElm = fixture.hostElement;
|
|
||||||
const innerCompElm = hostElm.querySelector('inner-comp');
|
|
||||||
const component = fixture.component;
|
|
||||||
|
|
||||||
expect(getComponent(innerCompElm !) !).toBe(component);
|
|
||||||
expect(getComponent(hostElm) !).toBeFalsy();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getHostElement', () => {
|
||||||
|
it('should return element on component', () => {
|
||||||
|
expect(getHostElement(myApp[0])).toEqual(fixture.hostElement);
|
||||||
|
expect(getHostElement(childComponent[0])).toEqual(child[0]);
|
||||||
|
expect(getHostElement(childComponent[1])).toEqual(child[1]);
|
||||||
|
});
|
||||||
|
it('should return element on directive', () => {
|
||||||
|
expect(getHostElement(dirA[0])).toEqual(div[0]);
|
||||||
|
expect(getHostElement(dirA[1])).toEqual(child[1]);
|
||||||
|
});
|
||||||
|
it('should throw on unknown target', () => {
|
||||||
|
expect(() => getHostElement({})).toThrowError(); //
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getInjector', () => {
|
||||||
|
it('should return node-injector from element', () => {
|
||||||
|
expect(getInjector(fixture.hostElement).get(String)).toEqual('Module');
|
||||||
|
expect(getInjector(child[0]).get(String)).toEqual('Child');
|
||||||
|
expect(getInjector(p[0]).get(String)).toEqual('Child');
|
||||||
|
});
|
||||||
|
it('should return node-injector from component with providers', () => {
|
||||||
|
expect(getInjector(myApp[0]).get(String)).toEqual('Module');
|
||||||
|
expect(getInjector(childComponent[0]).get(String)).toEqual('Child');
|
||||||
|
expect(getInjector(childComponent[1]).get(String)).toEqual('Child');
|
||||||
|
});
|
||||||
|
it('should return node-injector from directive without providers', () => {
|
||||||
|
expect(getInjector(dirA[0]).get(String)).toEqual('Module');
|
||||||
|
expect(getInjector(dirA[1]).get(String)).toEqual('Child');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getDirectives', () => {
|
||||||
|
it('should return empty array if no directives', () => {
|
||||||
|
expect(getDirectives(fixture.hostElement)).toEqual([]);
|
||||||
|
expect(getDirectives(span[0])).toEqual([]);
|
||||||
|
expect(getDirectives(child[0])).toEqual([]);
|
||||||
|
});
|
||||||
|
it('should return just directives', () => {
|
||||||
|
expect(getDirectives(div[0])).toEqual([dirA[0]]);
|
||||||
|
expect(getDirectives(child[1])).toEqual([dirA[1]]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getViewComponent', () => {
|
||||||
|
it('should return null when called on root component', () => {
|
||||||
|
expect(getViewComponent(fixture.hostElement)).toEqual(null);
|
||||||
|
expect(getViewComponent(myApp[0])).toEqual(null);
|
||||||
|
});
|
||||||
|
it('should return containing component of child component', () => {
|
||||||
|
expect(getViewComponent<MyApp>(child[0])).toEqual(myApp[0]);
|
||||||
|
expect(getViewComponent<MyApp>(child[1])).toEqual(myApp[0]);
|
||||||
|
expect(getViewComponent<MyApp>(child[2])).toEqual(myApp[0]);
|
||||||
|
|
||||||
|
expect(getViewComponent<MyApp>(childComponent[0])).toEqual(myApp[0]);
|
||||||
|
expect(getViewComponent<MyApp>(childComponent[1])).toEqual(myApp[0]);
|
||||||
|
expect(getViewComponent<MyApp>(childComponent[2])).toEqual(myApp[0]);
|
||||||
|
});
|
||||||
|
it('should return containing component of any view element', () => {
|
||||||
|
expect(getViewComponent<MyApp>(span[0])).toEqual(myApp[0]);
|
||||||
|
expect(getViewComponent<MyApp>(div[0])).toEqual(myApp[0]);
|
||||||
|
expect(getViewComponent<Child>(p[0])).toEqual(childComponent[0]);
|
||||||
|
expect(getViewComponent<Child>(p[1])).toEqual(childComponent[1]);
|
||||||
|
expect(getViewComponent<Child>(p[2])).toEqual(childComponent[2]);
|
||||||
|
});
|
||||||
|
it('should return containing component of child directive', () => {
|
||||||
|
expect(getViewComponent<MyApp>(dirA[0])).toEqual(myApp[0]);
|
||||||
|
expect(getViewComponent<MyApp>(dirA[1])).toEqual(myApp[0]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('localRefs', () => {
|
||||||
|
it('should retrieve empty map', () => {
|
||||||
|
expect(getLocalRefs(fixture.hostElement)).toEqual({});
|
||||||
|
expect(getLocalRefs(myApp[0])).toEqual({});
|
||||||
|
expect(getLocalRefs(span[0])).toEqual({});
|
||||||
|
expect(getLocalRefs(child[0])).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should retrieve the local map', () => {
|
||||||
|
expect(getLocalRefs(div[0])).toEqual({div: div[0], foo: dirA[0]});
|
||||||
|
expect(getLocalRefs(dirA[0])).toEqual({div: div[0], foo: dirA[0]});
|
||||||
|
|
||||||
|
expect(getLocalRefs(child[1])).toEqual({child: childComponent[1]});
|
||||||
|
expect(getLocalRefs(dirA[1])).toEqual({child: childComponent[1]});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getRootComponents', () => {
|
||||||
|
it('should return root components from component', () => {
|
||||||
|
const rootComponents = [myApp[0]];
|
||||||
|
expect(getRootComponents(myApp[0])).toEqual(rootComponents);
|
||||||
|
expect(getRootComponents(childComponent[0])).toEqual(rootComponents);
|
||||||
|
expect(getRootComponents(childComponent[1])).toEqual(rootComponents);
|
||||||
|
expect(getRootComponents(dirA[0])).toEqual(rootComponents);
|
||||||
|
expect(getRootComponents(dirA[1])).toEqual(rootComponents);
|
||||||
|
expect(getRootComponents(child[0])).toEqual(rootComponents);
|
||||||
|
expect(getRootComponents(child[1])).toEqual(rootComponents);
|
||||||
|
expect(getRootComponents(div[0])).toEqual(rootComponents);
|
||||||
|
expect(getRootComponents(p[0])).toEqual(rootComponents);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('markDirty', () => {
|
||||||
|
it('should re-render component', () => {
|
||||||
|
expect(span[0].textContent).toEqual('INIT');
|
||||||
|
myApp[0].text = 'WORKS';
|
||||||
|
markDirty(myApp[0]);
|
||||||
|
fixture.requestAnimationFrame.flush();
|
||||||
|
expect(span[0].textContent).toEqual('WORKS');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('discovery utils deprecated', () => {
|
||||||
|
|
||||||
describe('getRootComponents()', () => {
|
describe('getRootComponents()', () => {
|
||||||
it('should return a list of the root components of the application from an element', () => {
|
it('should return a list of the root components of the application from an element', () => {
|
||||||
let innerComp: InnerComp;
|
let innerComp: InnerComp;
|
||||||
|
@ -171,55 +375,6 @@ describe('discovery utils', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getHostComponent()', () => {
|
|
||||||
it('should return the component instance for a DOM element', () => {
|
|
||||||
let innerComp: InnerComp;
|
|
||||||
|
|
||||||
class InnerComp {
|
|
||||||
static ngComponentDef = defineComponent({
|
|
||||||
type: InnerComp,
|
|
||||||
selectors: [['inner-comp']],
|
|
||||||
factory: () => innerComp = new InnerComp(),
|
|
||||||
consts: 1,
|
|
||||||
vars: 0,
|
|
||||||
template: (rf: RenderFlags, ctx: InnerComp) => {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
element(0, 'div');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class Comp {
|
|
||||||
static ngComponentDef = defineComponent({
|
|
||||||
type: Comp,
|
|
||||||
selectors: [['comp']],
|
|
||||||
factory: () => new Comp(),
|
|
||||||
consts: 1,
|
|
||||||
vars: 0,
|
|
||||||
template: (rf: RenderFlags, ctx: Comp) => {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
element(0, 'inner-comp');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
directives: [InnerComp]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(Comp);
|
|
||||||
fixture.update();
|
|
||||||
|
|
||||||
const hostElm = fixture.hostElement;
|
|
||||||
const innerElm = hostElm.querySelector('inner-comp') !;
|
|
||||||
const divElm = hostElm.querySelector('div') !;
|
|
||||||
const component = fixture.component;
|
|
||||||
|
|
||||||
expect(getHostComponent(hostElm) !).toBe(component);
|
|
||||||
expect(getHostComponent(innerElm) !).toBe(innerComp !);
|
|
||||||
expect(getHostComponent(divElm) !).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getInjector', () => {
|
describe('getInjector', () => {
|
||||||
|
|
||||||
it('should return an injector that can return directive instances', () => {
|
it('should return an injector that can return directive instances', () => {
|
||||||
|
|
|
@ -5,7 +5,9 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {getComponent, getDirectives, getHostComponent, getInjector, getRootComponents} from '../../src/render3/discovery_utils';
|
import {ɵmarkDirty as markDirty} from '@angular/core';
|
||||||
|
|
||||||
|
import {getComponent, getDirectives, getHostElement, getInjector, getRootComponents, getViewComponent} from '../../src/render3/discovery_utils';
|
||||||
import {GLOBAL_PUBLISH_EXPANDO_KEY, GlobalDevModeContainer, publishDefaultGlobalUtils, publishGlobalUtil} from '../../src/render3/global_utils';
|
import {GLOBAL_PUBLISH_EXPANDO_KEY, GlobalDevModeContainer, publishDefaultGlobalUtils, publishGlobalUtil} from '../../src/render3/global_utils';
|
||||||
import {getPlayers} from '../../src/render3/players';
|
import {getPlayers} from '../../src/render3/players';
|
||||||
import {global} from '../../src/util';
|
import {global} from '../../src/util';
|
||||||
|
@ -26,16 +28,21 @@ describe('global utils', () => {
|
||||||
|
|
||||||
it('should publish getComponent', () => { assertPublished('getComponent', getComponent); });
|
it('should publish getComponent', () => { assertPublished('getComponent', getComponent); });
|
||||||
|
|
||||||
|
it('should publish getViewComponent',
|
||||||
|
() => { assertPublished('getViewComponent', getViewComponent); });
|
||||||
|
|
||||||
it('should publish getRootComponents',
|
it('should publish getRootComponents',
|
||||||
() => { assertPublished('getRootComponents', getRootComponents); });
|
() => { assertPublished('getRootComponents', getRootComponents); });
|
||||||
|
|
||||||
it('should publish getDirectives', () => { assertPublished('getDirectives', getDirectives); });
|
it('should publish getDirectives', () => { assertPublished('getDirectives', getDirectives); });
|
||||||
|
|
||||||
it('should publish getHostComponent',
|
it('should publish getHostComponent',
|
||||||
() => { assertPublished('getHostComponent', getHostComponent); });
|
() => { assertPublished('getHostElement', getHostElement); });
|
||||||
|
|
||||||
it('should publish getInjector', () => { assertPublished('getInjector', getInjector); });
|
it('should publish getInjector', () => { assertPublished('getInjector', getInjector); });
|
||||||
|
|
||||||
|
it('should publish markDirty', () => { assertPublished('markDirty', markDirty); });
|
||||||
|
|
||||||
it('should publish getPlayers', () => { assertPublished('getPlayers', getPlayers); });
|
it('should publish getPlayers', () => { assertPublished('getPlayers', getPlayers); });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -20,7 +20,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_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 {SWITCH_RENDERER2_FACTORY__POST_R3__ as R3_RENDERER2_FACTORY} from '../../src/render/api';
|
||||||
import {CreateComponentOptions} from '../../src/render3/component';
|
import {CreateComponentOptions} from '../../src/render3/component';
|
||||||
import {discoverDirectives, getContext, isComponentInstance} from '../../src/render3/context_discovery';
|
import {getContext, getDirectivesAtNodeIndex, isComponentInstance} from '../../src/render3/context_discovery';
|
||||||
import {extractDirectiveDef, extractPipeDef} from '../../src/render3/definition';
|
import {extractDirectiveDef, extractPipeDef} from '../../src/render3/definition';
|
||||||
import {NG_ELEMENT_ID} from '../../src/render3/fields';
|
import {NG_ELEMENT_ID} from '../../src/render3/fields';
|
||||||
import {ComponentTemplate, ComponentType, DirectiveDef, DirectiveType, RenderFlags, defineComponent, defineDirective, renderComponent as _renderComponent, tick} from '../../src/render3/index';
|
import {ComponentTemplate, ComponentType, DirectiveDef, DirectiveType, RenderFlags, defineComponent, defineDirective, renderComponent as _renderComponent, tick} from '../../src/render3/index';
|
||||||
|
@ -319,7 +319,7 @@ export function createDirective(
|
||||||
|
|
||||||
/** Gets the directive on the given node at the given index */
|
/** Gets the directive on the given node at the given index */
|
||||||
export function getDirectiveOnNode(nodeIndex: number, dirIndex: number = 0) {
|
export function getDirectiveOnNode(nodeIndex: number, dirIndex: number = 0) {
|
||||||
const directives = discoverDirectives(nodeIndex + HEADER_OFFSET, getViewData(), true);
|
const directives = getDirectivesAtNodeIndex(nodeIndex + HEADER_OFFSET, getViewData(), true);
|
||||||
if (directives == null) {
|
if (directives == null) {
|
||||||
throw new Error(`No directives exist on node in slot ${nodeIndex}`);
|
throw new Error(`No directives exist on node in slot ${nodeIndex}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ describe('style and class based bindings', () => {
|
||||||
const rootContext =
|
const rootContext =
|
||||||
createRootContext(requestAnimationFrame.bind(window), playerHandler || null);
|
createRootContext(requestAnimationFrame.bind(window), playerHandler || null);
|
||||||
const lViewData = createLViewData(
|
const lViewData = createLViewData(
|
||||||
domRendererFactory3.createRenderer(element, null),
|
null, domRendererFactory3.createRenderer(element, null),
|
||||||
createTView(-1, null, 1, 0, null, null, null), rootContext, LViewFlags.IsRoot);
|
createTView(-1, null, 1, 0, null, null, null), rootContext, LViewFlags.IsRoot);
|
||||||
return lViewData;
|
return lViewData;
|
||||||
}
|
}
|
||||||
|
|
|
@ -454,6 +454,7 @@ export declare class KeyValueDiffers {
|
||||||
/** @deprecated */ factories: KeyValueDifferFactory[];
|
/** @deprecated */ factories: KeyValueDifferFactory[];
|
||||||
constructor(factories: KeyValueDifferFactory[]);
|
constructor(factories: KeyValueDifferFactory[]);
|
||||||
find(kv: any): KeyValueDifferFactory;
|
find(kv: any): KeyValueDifferFactory;
|
||||||
|
static ngInjectableDef: never;
|
||||||
static create<S>(factories: KeyValueDifferFactory[], parent?: KeyValueDiffers): KeyValueDiffers;
|
static create<S>(factories: KeyValueDifferFactory[], parent?: KeyValueDiffers): KeyValueDiffers;
|
||||||
static extend<S>(factories: KeyValueDifferFactory[]): StaticProvider;
|
static extend<S>(factories: KeyValueDifferFactory[]): StaticProvider;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
export declare function getComponent<T = {}>(target: {}): T | null;
|
export declare function getComponent<T = {}>(element: Element): T | null;
|
||||||
|
|
||||||
export declare function getDirectives(target: {}): Array<{}>;
|
export declare function getDirectives(target: {}): Array<{}>;
|
||||||
|
|
||||||
export declare function getHostComponent<T = {}>(target: {}): T | null;
|
export declare function getHostElement<T>(directive: T): Element;
|
||||||
|
|
||||||
export declare function getInjector(target: {}): Injector;
|
export declare function getInjector(target: {}): Injector;
|
||||||
|
|
||||||
export declare function getPlayers(ref: ComponentInstance | DirectiveInstance | HTMLElement): Player[];
|
export declare function getPlayers(ref: ComponentInstance | DirectiveInstance | HTMLElement): Player[];
|
||||||
|
|
||||||
export declare function getRootComponents(target: {}): any[];
|
export declare function getRootComponents(target: {}): any[];
|
||||||
|
|
||||||
|
export declare function getViewComponent<T = {}>(element: Element | {}): T | null;
|
||||||
|
|
||||||
|
export declare function markDirty<T>(component: T): void;
|
||||||
|
|
Loading…
Reference in New Issue