2018-09-24 14:51:54 -07:00
|
|
|
/**
|
|
|
|
|
* @license
|
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* 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 {Injector} from '../di/injector';
|
|
|
|
|
|
|
|
|
|
import {assertDefined} from './assert';
|
2018-11-15 08:43:56 -08:00
|
|
|
import {discoverLocalRefs, getComponentAtNodeIndex, getContext, getDirectivesAtNodeIndex} from './context_discovery';
|
2018-10-12 15:02:54 -07:00
|
|
|
import {LContext} from './interfaces/context';
|
2018-11-15 08:43:56 -08:00
|
|
|
import {TElementNode} from './interfaces/node';
|
2018-11-21 21:14:06 -08:00
|
|
|
import {CONTEXT, FLAGS, HOST, LView, LViewFlags, PARENT, RootContext, TVIEW} from './interfaces/view';
|
|
|
|
|
import {readPatchedLView, stringify} from './util';
|
2018-10-18 09:23:18 +02:00
|
|
|
import {NodeInjector} from './view_engine_compatibility';
|
|
|
|
|
|
2018-10-12 15:02:54 -07:00
|
|
|
|
2018-09-24 14:51:54 -07:00
|
|
|
/**
|
2018-11-15 08:43:56 -08:00
|
|
|
* Returns the component instance associated with a given DOM host element.
|
|
|
|
|
* Elements which don't represent components return `null`.
|
2018-09-24 14:51:54 -07:00
|
|
|
*
|
2018-11-15 08:43:56 -08:00
|
|
|
* @param element Host DOM element from which the component should be retrieved for.
|
|
|
|
|
*
|
|
|
|
|
* ```
|
|
|
|
|
* <my-app>
|
|
|
|
|
* #VIEW
|
|
|
|
|
* <div>
|
|
|
|
|
* <child-comp></child-comp>
|
|
|
|
|
* </div>
|
|
|
|
|
* </mp-app>
|
2018-09-24 14:51:54 -07:00
|
|
|
*
|
2018-11-15 08:43:56 -08:00
|
|
|
* expect(getComponent(<child-comp>) instanceof ChildComponent).toBeTruthy();
|
|
|
|
|
* expect(getComponent(<my-app>) instanceof MyApp).toBeTruthy();
|
|
|
|
|
* ```
|
2018-11-08 18:25:33 +01:00
|
|
|
*
|
|
|
|
|
* @publicApi
|
2018-09-24 14:51:54 -07:00
|
|
|
*/
|
2018-11-15 08:43:56 -08:00
|
|
|
export function getComponent<T = {}>(element: Element): T|null {
|
|
|
|
|
if (!(element instanceof Node)) throw new Error('Expecting instance of DOM Node');
|
|
|
|
|
|
|
|
|
|
const context = loadContext(element) !;
|
2018-09-24 14:51:54 -07:00
|
|
|
|
|
|
|
|
if (context.component === undefined) {
|
2018-11-21 21:14:06 -08:00
|
|
|
context.component = getComponentAtNodeIndex(context.nodeIndex, context.lView);
|
2018-09-24 14:51:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return context.component as T;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2018-11-15 08:43:56 -08:00
|
|
|
* Returns the component instance associated with view which owns the DOM element (`null`
|
|
|
|
|
* otherwise).
|
|
|
|
|
*
|
|
|
|
|
* @param element DOM element which is owned by an existing component's view.
|
|
|
|
|
*
|
|
|
|
|
* ```
|
|
|
|
|
* <my-app>
|
|
|
|
|
* #VIEW
|
|
|
|
|
* <div>
|
|
|
|
|
* <child-comp></child-comp>
|
|
|
|
|
* </div>
|
|
|
|
|
* </mp-app>
|
2018-09-24 14:51:54 -07:00
|
|
|
*
|
2018-11-15 08:43:56 -08:00
|
|
|
* expect(getViewComponent(<child-comp>) instanceof MyApp).toBeTruthy();
|
|
|
|
|
* expect(getViewComponent(<my-app>)).toEqual(null);
|
|
|
|
|
* ```
|
2018-11-08 18:25:33 +01:00
|
|
|
*
|
|
|
|
|
* @publicApi
|
2018-09-24 14:51:54 -07:00
|
|
|
*/
|
2018-11-15 08:43:56 -08:00
|
|
|
export function getViewComponent<T = {}>(element: Element | {}): T|null {
|
|
|
|
|
const context = loadContext(element) !;
|
2018-11-21 21:14:06 -08:00
|
|
|
let lView: LView = context.lView;
|
2018-11-15 08:43:56 -08:00
|
|
|
while (lView[PARENT] && lView[HOST] === null) {
|
|
|
|
|
// As long as lView[HOST] is null we know we are part of sub-template such as `*ngIf`
|
|
|
|
|
lView = lView[PARENT] !;
|
2018-09-24 14:51:54 -07:00
|
|
|
}
|
2018-11-15 08:43:56 -08:00
|
|
|
|
|
|
|
|
return lView[FLAGS] & LViewFlags.IsRoot ? null : lView[CONTEXT] as T;
|
2018-09-24 14:51:54 -07:00
|
|
|
}
|
|
|
|
|
|
2018-11-15 08:43:56 -08:00
|
|
|
|
|
|
|
|
|
2018-09-24 14:51:54 -07:00
|
|
|
/**
|
|
|
|
|
* Returns the `RootContext` instance that is associated with
|
|
|
|
|
* the application where the target is situated.
|
2018-11-08 18:25:33 +01:00
|
|
|
*
|
2018-09-24 14:51:54 -07:00
|
|
|
*/
|
2018-11-21 21:14:06 -08:00
|
|
|
export function getRootContext(target: LView | {}): RootContext {
|
|
|
|
|
const lView = Array.isArray(target) ? target : loadContext(target) !.lView;
|
|
|
|
|
const rootLView = getRootView(lView);
|
|
|
|
|
return rootLView[CONTEXT] as RootContext;
|
2018-09-24 14:51:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2018-11-15 08:43:56 -08:00
|
|
|
* Retrieve all root components.
|
|
|
|
|
*
|
|
|
|
|
* Root components are those which have been bootstrapped by Angular.
|
|
|
|
|
*
|
|
|
|
|
* @param target A DOM element, component or directive instance.
|
2018-11-08 18:25:33 +01:00
|
|
|
*
|
|
|
|
|
* @publicApi
|
2018-09-24 14:51:54 -07:00
|
|
|
*/
|
|
|
|
|
export function getRootComponents(target: {}): any[] {
|
|
|
|
|
return [...getRootContext(target).components];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2018-11-15 08:43:56 -08:00
|
|
|
* Retrieves an `Injector` associated with the element, component or directive.
|
|
|
|
|
*
|
|
|
|
|
* @param target A DOM element, component or directive instance.
|
2018-11-08 18:25:33 +01:00
|
|
|
*
|
|
|
|
|
* @publicApi
|
2018-09-24 14:51:54 -07:00
|
|
|
*/
|
2018-10-02 14:42:34 +02:00
|
|
|
export function getInjector(target: {}): Injector {
|
|
|
|
|
const context = loadContext(target);
|
2018-11-21 21:14:06 -08:00
|
|
|
const tNode = context.lView[TVIEW].data[context.nodeIndex] as TElementNode;
|
2018-10-02 14:42:34 +02:00
|
|
|
|
2018-11-21 21:14:06 -08:00
|
|
|
return new NodeInjector(tNode, context.lView);
|
2018-09-24 14:51:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2018-11-15 08:43:56 -08:00
|
|
|
* Retrieves directives associated with a given DOM host element.
|
|
|
|
|
*
|
|
|
|
|
* @param target A DOM element, component or directive instance.
|
2018-11-08 18:25:33 +01:00
|
|
|
*
|
|
|
|
|
* @publicApi
|
2018-09-24 14:51:54 -07:00
|
|
|
*/
|
|
|
|
|
export function getDirectives(target: {}): Array<{}> {
|
|
|
|
|
const context = loadContext(target) !;
|
|
|
|
|
|
|
|
|
|
if (context.directives === undefined) {
|
2018-11-21 21:14:06 -08:00
|
|
|
context.directives = getDirectivesAtNodeIndex(context.nodeIndex, context.lView, false);
|
2018-09-24 14:51:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return context.directives || [];
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-24 10:30:29 +02:00
|
|
|
/**
|
|
|
|
|
* Returns LContext associated with a target passed as an argument.
|
|
|
|
|
* Throws if a given target doesn't have associated LContext.
|
2018-11-08 18:25:33 +01:00
|
|
|
*
|
2018-09-24 10:30:29 +02:00
|
|
|
*/
|
2018-11-21 21:14:06 -08:00
|
|
|
export function loadContext(target: {}): LContext;
|
|
|
|
|
export function loadContext(target: {}, throwOnNotFound: false): LContext|null;
|
|
|
|
|
export function loadContext(target: {}, throwOnNotFound: boolean = true): LContext|null {
|
2018-09-24 14:51:54 -07:00
|
|
|
const context = getContext(target);
|
2018-11-21 21:14:06 -08:00
|
|
|
if (!context && throwOnNotFound) {
|
2018-09-24 14:51:54 -07:00
|
|
|
throw new Error(
|
2018-11-15 08:43:56 -08:00
|
|
|
ngDevMode ? `Unable to find context associated with ${stringify(target)}` :
|
2018-09-24 14:51:54 -07:00
|
|
|
'Invalid ng target');
|
|
|
|
|
}
|
|
|
|
|
return context;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2018-11-21 21:14:06 -08:00
|
|
|
* Retrieve the root view from any component by walking the parent `LView` until
|
|
|
|
|
* reaching the root `LView`.
|
2018-09-24 14:51:54 -07:00
|
|
|
*
|
|
|
|
|
* @param componentOrView any component or view
|
2018-11-08 18:25:33 +01:00
|
|
|
*
|
2018-09-24 14:51:54 -07:00
|
|
|
*/
|
2018-11-21 21:14:06 -08:00
|
|
|
export function getRootView(componentOrView: LView | {}): LView {
|
|
|
|
|
let lView: LView;
|
2018-09-24 14:51:54 -07:00
|
|
|
if (Array.isArray(componentOrView)) {
|
2018-11-21 21:14:06 -08:00
|
|
|
ngDevMode && assertDefined(componentOrView, 'lView');
|
|
|
|
|
lView = componentOrView as LView;
|
2018-09-24 14:51:54 -07:00
|
|
|
} else {
|
|
|
|
|
ngDevMode && assertDefined(componentOrView, 'component');
|
2018-11-21 21:14:06 -08:00
|
|
|
lView = readPatchedLView(componentOrView) !;
|
2018-09-24 14:51:54 -07:00
|
|
|
}
|
2018-11-21 21:14:06 -08:00
|
|
|
while (lView && !(lView[FLAGS] & LViewFlags.IsRoot)) {
|
|
|
|
|
lView = lView[PARENT] !;
|
2018-09-24 14:51:54 -07:00
|
|
|
}
|
2018-11-21 21:14:06 -08:00
|
|
|
return lView;
|
2018-09-24 14:51:54 -07:00
|
|
|
}
|
2018-09-26 15:21:58 +02:00
|
|
|
|
|
|
|
|
/**
|
2018-11-15 08:43:56 -08:00
|
|
|
* 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.
|
2018-11-08 18:25:33 +01:00
|
|
|
*
|
|
|
|
|
* @publicApi
|
2018-09-26 15:21:58 +02:00
|
|
|
*/
|
|
|
|
|
export function getLocalRefs(target: {}): {[key: string]: any} {
|
|
|
|
|
const context = loadContext(target) !;
|
|
|
|
|
|
|
|
|
|
if (context.localRefs === undefined) {
|
2018-11-21 21:14:06 -08:00
|
|
|
context.localRefs = discoverLocalRefs(context.lView, context.nodeIndex);
|
2018-09-26 15:21:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return context.localRefs || {};
|
2018-10-04 20:40:39 -07:00
|
|
|
}
|
2018-11-15 08:43:56 -08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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 || '';
|
|
|
|
|
}
|