diff --git a/aio/tools/transforms/angular-api-package/index.js b/aio/tools/transforms/angular-api-package/index.js index 3931a8b897..eea0910068 100644 --- a/aio/tools/transforms/angular-api-package/index.js +++ b/aio/tools/transforms/angular-api-package/index.js @@ -97,7 +97,7 @@ module.exports = 'common/testing/index.ts', 'common/upgrade/index.ts', 'core/index.ts', - 'core/global.ts', + 'core/global/index.ts', 'core/testing/index.ts', 'elements/index.ts', 'forms/index.ts', diff --git a/packages/core/global.ts b/packages/core/global.ts deleted file mode 100644 index c91a05e544..0000000000 --- a/packages/core/global.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * @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 - */ diff --git a/packages/core/global/PACKAGE.md b/packages/core/global/PACKAGE.md new file mode 100644 index 0000000000..d278b8586f --- /dev/null +++ b/packages/core/global/PACKAGE.md @@ -0,0 +1,5 @@ +Exposes a set of functions in the global namespace which are useful for debugging the current state +of your application. +These functions are exposed via the global `ng` "namespace" variable automatically when you import +from `@angular/core` and run your application in development mode. These functions are not exposed +when the application runs in a production mode. diff --git a/packages/core/global/index.ts b/packages/core/global/index.ts new file mode 100644 index 0000000000..874b4bde3a --- /dev/null +++ b/packages/core/global/index.ts @@ -0,0 +1,11 @@ +/** + * @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 + */ + +// The global utilities are re-exported through here so that they get their own separate `global` +// section in the API docs which makes it more visible that they can't be imported directly. +export * from '../src/render3/global_utils_api'; diff --git a/packages/core/src/debug/debug_node.ts b/packages/core/src/debug/debug_node.ts index 26f8042cbf..3270a568f1 100644 --- a/packages/core/src/debug/debug_node.ts +++ b/packages/core/src/debug/debug_node.ts @@ -7,18 +7,18 @@ */ import {Injector} from '../di'; -import {getViewComponent} from '../render3/global_utils_api'; import {CONTAINER_HEADER_OFFSET, LContainer, NATIVE} from '../render3/interfaces/container'; import {TElementNode, TNode, TNodeFlags, TNodeType} from '../render3/interfaces/node'; import {isComponentHost, isLContainer} from '../render3/interfaces/type_checks'; import {DECLARATION_COMPONENT_VIEW, LView, PARENT, TData, TVIEW, T_HOST} from '../render3/interfaces/view'; -import {getComponent, getContext, getInjectionTokens, getInjector, getListeners, getLocalRefs, isBrowserEvents, loadLContext} from '../render3/util/discovery_utils'; +import {getComponent, getContext, getInjectionTokens, getInjector, getListeners, getLocalRefs, getOwningComponent, loadLContext} from '../render3/util/discovery_utils'; import {INTERPOLATION_DELIMITER, renderStringify} from '../render3/util/misc_utils'; import {getComponentLViewByIndex, getNativeByTNodeOrNull} from '../render3/util/view_utils'; import {assertDomNode} from '../util/assert'; import {DebugContext} from '../view/index'; + /** * @publicApi */ @@ -217,14 +217,14 @@ class DebugNode__POST_R3__ implements DebugNode { get componentInstance(): any { const nativeElement = this.nativeNode; return nativeElement && - (getComponent(nativeElement as Element) || getViewComponent(nativeElement)); + (getComponent(nativeElement as Element) || getOwningComponent(nativeElement)); } get context(): any { return getComponent(this.nativeNode as Element) || getContext(this.nativeNode as Element); } get listeners(): DebugEventListener[] { - return getListeners(this.nativeNode as Element).filter(isBrowserEvents); + return getListeners(this.nativeNode as Element).filter(listener => listener.type === 'dom'); } get references(): {[key: string]: any;} { return getLocalRefs(this.nativeNode); } diff --git a/packages/core/src/render3/global_utils_api.ts b/packages/core/src/render3/global_utils_api.ts index 1884a8e410..82f683fd0f 100644 --- a/packages/core/src/render3/global_utils_api.ts +++ b/packages/core/src/render3/global_utils_api.ts @@ -16,4 +16,5 @@ */ export {markDirty} from './instructions/all'; -export {getComponent, getContext, getDebugNode, getDirectives, getHostElement, getInjector, getListeners, getRootComponents, getViewComponent} from './util/discovery_utils'; +export {applyChanges} from './util/change_detection_utils'; +export {Listener, getComponent, getContext, getDirectives, getHostElement, getInjector, getListeners, getOwningComponent, getRootComponents} from './util/discovery_utils'; diff --git a/packages/core/src/render3/instructions/change_detection.ts b/packages/core/src/render3/instructions/change_detection.ts index fc4c9e50d7..bb9ad7ba5a 100644 --- a/packages/core/src/render3/instructions/change_detection.ts +++ b/packages/core/src/render3/instructions/change_detection.ts @@ -15,44 +15,35 @@ import {detectChangesInternal, markViewDirty, scheduleTick, tickRootContext} fro /** * Synchronously perform change detection on a component (and possibly its sub-components). * - * This function triggers change detection in a synchronous way on a component. There should - * be very little reason to call this function directly since a preferred way to do change - * detection is to {@link markDirty} the component and wait for the scheduler to call this method - * at some future point in time. This is because a single user action often results in many - * components being invalidated and calling change detection on each component synchronously - * would be inefficient. It is better to wait until all components are marked as dirty and - * then perform single change detection across all of the components + * This function triggers change detection in a synchronous way on a component. * * @param component The component which the change detection should be performed on. */ -export function detectChanges(component: T): void { +export function detectChanges(component: {}): void { const view = getComponentViewByInstance(component); - detectChangesInternal(view, component); + detectChangesInternal(view, component); } /** - * Mark the component as dirty (needing change detection). + * Marks the component as dirty (needing change detection). Marking a component dirty will + * schedule a change detection on it at some point in the future. * - * Marking a component dirty will schedule a change detection on this - * component at some point in the future. Marking an already dirty - * component as dirty is a noop. Only one outstanding change detection - * can be scheduled per component tree. (Two components bootstrapped with - * separate `renderComponent` will have separate schedulers) - * - * When the root component is bootstrapped with `renderComponent`, a scheduler - * can be provided. + * Marking an already dirty component as dirty won't do anything. Only one outstanding change + * detection can be scheduled per component tree. * * @param component Component to mark as dirty. * * @publicApi + * @globalApi ng */ -export function markDirty(component: T) { +export function markDirty(component: {}): void { ngDevMode && assertDefined(component, 'component'); const rootView = markViewDirty(getComponentViewByInstance(component)) !; ngDevMode && assertDefined(rootView[CONTEXT], 'rootContext should be defined'); scheduleTick(rootView[CONTEXT] as RootContext, RootContextFlags.DetectChanges); } + /** * Used to perform change detection on the whole application. * diff --git a/packages/core/src/render3/pipe.ts b/packages/core/src/render3/pipe.ts index 30ba298e53..d4b5b9c02d 100644 --- a/packages/core/src/render3/pipe.ts +++ b/packages/core/src/render3/pipe.ts @@ -57,8 +57,6 @@ export function ɵɵpipe(index: number, pipeName: string): any { * @param name Name of pipe to resolve * @param registry Full list of available pipes * @returns Matching PipeDef - * - * @publicApi */ function getPipeDef(name: string, registry: PipeDefList | null): PipeDef { if (registry) { diff --git a/packages/core/src/render3/util/change_detection_utils.ts b/packages/core/src/render3/util/change_detection_utils.ts new file mode 100644 index 0000000000..30c5b4a20a --- /dev/null +++ b/packages/core/src/render3/util/change_detection_utils.ts @@ -0,0 +1,24 @@ +/** + * @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 {detectChanges, markDirty} from '../instructions/all'; +import {getRootComponents} from './discovery_utils'; + +/** + * Marks a component for check (in case of OnPush components) and synchronously + * performs change detection on the application this component belongs to. + * + * @param component Component to {@link ChangeDetectorRef#markForCheck mark for check}. + * + * @publicApi + * @globalApi ng + */ +export function applyChanges(component: {}): void { + markDirty(component); + getRootComponents(component).forEach(rootComponent => detectChanges(rootComponent)); +} diff --git a/packages/core/src/render3/util/discovery_utils.ts b/packages/core/src/render3/util/discovery_utils.ts index 4553e88923..2a03289758 100644 --- a/packages/core/src/render3/util/discovery_utils.ts +++ b/packages/core/src/render3/util/discovery_utils.ts @@ -24,31 +24,35 @@ import {getTNode, unwrapRNode} from './view_utils'; /** - * Returns the component instance associated with a given DOM host element. - * Elements which don't represent components return `null`. + * Retrieves the component instance associated with a given DOM element. * - * @param element Host DOM element from which the component should be retrieved. - * - * ``` + * @usageNotes + * Given the following DOM structure: + * ```html * - * #VIEW - *
- * - *
- * - * - * expect(getComponent() instanceof ChildComponent).toBeTruthy(); - * expect(getComponent() instanceof MyApp).toBeTruthy(); + *
+ * + *
+ *
* ``` + * Calling `getComponent` on `` will return the instance of `ChildComponent` + * associated with this DOM element. + * + * Calling the function on `` will return the `MyApp` instance. + * + * + * @param element DOM element from which the component should be retrieved. + * @returns Component instance associated with the element or `null` if there + * is no component associated with it. * * @publicApi + * @globalApi ng */ -export function getComponent(element: Element): T|null { - if (!(element instanceof Node)) throw new Error('Expecting instance of DOM Node'); +export function getComponent(element: Element): T|null { + assertDomElement(element); const context = loadLContext(element, false); if (context === null) return null; - if (context.component === undefined) { context.component = getComponentAtNodeIndex(context.nodeIndex, context.lView); } @@ -56,56 +60,42 @@ export function getComponent(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`. + * If inside an embedded view (e.g. `*ngIf` or `*ngFor`), retrieves the context of the embedded + * view that the element is part of. Otherwise retrieves the instance of the component whose view + * owns the element (in this case, the result is the same as calling `getOwningComponent`). * - * @param element Host DOM element from which the component should be retrieved. - * - * ``` - * - * #VIEW - *
- * - *
- * - * - * expect(getComponent() instanceof ChildComponent).toBeTruthy(); - * expect(getComponent() instanceof MyApp).toBeTruthy(); - * ``` + * @param element Element for which to get the surrounding component instance. + * @returns Instance of the component that is around the element or null if the element isn't + * inside any component. * * @publicApi + * @globalApi ng */ -export function getContext(element: Element): T|null { - if (!(element instanceof Node)) throw new Error('Expecting instance of DOM Node'); +export function getContext(element: Element): T|null { + assertDomElement(element); const context = loadLContext(element, false); - if (context === null) return null; - - return context.lView[CONTEXT] as T; + return context === null ? null : context.lView[CONTEXT] as T; } /** - * Returns the component instance associated with view which owns the DOM element (`null` - * otherwise). + * Retrieves the component instance whose view contains the DOM element. * - * @param element DOM element which is owned by an existing component's view. + * For example, if `` is used in the template of `` + * (i.e. a `ViewChild` of ``), calling `getOwningComponent` on `` + * would return ``. * - * ``` - * - * #VIEW - *
- * - *
- * - * - * expect(getViewComponent() instanceof MyApp).toBeTruthy(); - * expect(getViewComponent()).toEqual(null); - * ``` + * @param elementOrDir DOM element, component or directive instance + * for which to retrieve the root components. + * @returns Component instance whose view owns the DOM element or null if the element is not + * part of a component view. * * @publicApi + * @globalApi ng */ -export function getViewComponent(element: Element | {}): T|null { - const context = loadLContext(element, false); +export function getOwningComponent(elementOrDir: Element | {}): T|null { + const context = loadLContext(elementOrDir, false); if (context === null) return null; let lView = context.lView; @@ -119,27 +109,32 @@ export function getViewComponent(element: Element | {}): T|null { } /** - * Retrieve all root components. - * + * Retrieves all root components associated with a DOM element, directive or component instance. * Root components are those which have been bootstrapped by Angular. * - * @param target A DOM element, component or directive instance. + * @param elementOrDir DOM element, component or directive instance + * for which to retrieve the root components. + * @returns Root components associated with the target object. * * @publicApi + * @globalApi ng */ -export function getRootComponents(target: {}): any[] { - return [...getRootContext(target).components]; +export function getRootComponents(elementOrDir: Element | {}): {}[] { + return [...getRootContext(elementOrDir).components]; } /** - * Retrieves an `Injector` associated with the element, component or directive. + * Retrieves an `Injector` associated with an element, component or directive instance. * - * @param target A DOM element, component or directive instance. + * @param elementOrDir DOM element, component or directive instance for which to + * retrieve the injector. + * @returns Injector associated with the element, component or directive instance. * * @publicApi + * @globalApi ng */ -export function getInjector(target: {}): Injector { - const context = loadLContext(target, false); +export function getInjector(elementOrDir: Element | {}): Injector { + const context = loadLContext(elementOrDir, false); if (context === null) return Injector.NULL; const tNode = context.lView[TVIEW].data[context.nodeIndex] as TElementNode; @@ -150,7 +145,6 @@ export function getInjector(target: {}): Injector { * 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); @@ -176,26 +170,43 @@ export function getInjectionTokens(element: Element): any[] { } /** - * Retrieves directives associated with a given DOM host element. + * Retrieves directive instances associated with a given DOM element. Does not include + * component instances. * - * @param target A DOM element, component or directive instance. + * @usageNotes + * Given the following DOM structure: + * ``` + * + * + * + * + * ``` + * Calling `getDirectives` on `