diff --git a/packages/core/src/render3/hooks.ts b/packages/core/src/render3/hooks.ts index be76049088..89b36d80cd 100644 --- a/packages/core/src/render3/hooks.ts +++ b/packages/core/src/render3/hooks.ts @@ -10,20 +10,6 @@ import {DirectiveDef} from './interfaces/definition'; import {LNodeFlags} from './interfaces/node'; import {HookData, LView, LifecycleStage, TView} from './interfaces/view'; - - -/** Constants used by lifecycle hooks to determine when and how a hook should be called. */ -export const enum LifecycleHook { - ON_INIT = 0b00, - ON_CHECK = 0b01, - - /* Mask used to get the type of the lifecycle hook from flags in hook queue */ - TYPE_MASK = 0b00000000000000000000000000000001, - - /* Shift needed to get directive index from flags in hook queue */ - INDX_SHIFT = 1 -} - /** * If this is the first template pass, any ngOnInit or ngDoCheck hooks will be queued into * TView.initHooks during directiveCreate. @@ -40,11 +26,12 @@ export function queueInitHooks( index: number, onInit: (() => void) | null, doCheck: (() => void) | null, tView: TView): void { if (tView.firstTemplatePass === true) { if (onInit != null) { - (tView.initHooks || (tView.initHooks = [])).push(getInitFlags(index), onInit); + (tView.initHooks || (tView.initHooks = [])).push(index, onInit); } if (doCheck != null) { - (tView.initHooks || (tView.initHooks = [])).push(getCheckFlags(index), doCheck); + (tView.initHooks || (tView.initHooks = [])).push(index, doCheck); + (tView.checkHooks || (tView.checkHooks = [])).push(index, doCheck); } } } @@ -74,23 +61,24 @@ export function queueLifecycleHooks(flags: number, currentView: LView): void { /** Queues afterContentInit and afterContentChecked hooks on TView */ function queueContentHooks(def: DirectiveDef, tView: TView, i: number): void { if (def.afterContentInit != null) { - (tView.contentHooks || (tView.contentHooks = [])).push(getInitFlags(i), def.afterContentInit); + (tView.contentHooks || (tView.contentHooks = [])).push(i, def.afterContentInit); } if (def.afterContentChecked != null) { - (tView.contentHooks || (tView.contentHooks = [ - ])).push(getCheckFlags(i), def.afterContentChecked); + (tView.contentHooks || (tView.contentHooks = [])).push(i, def.afterContentChecked); + (tView.contentCheckHooks || (tView.contentCheckHooks = [])).push(i, def.afterContentChecked); } } /** Queues afterViewInit and afterViewChecked hooks on TView */ function queueViewHooks(def: DirectiveDef, tView: TView, i: number): void { if (def.afterViewInit != null) { - (tView.viewHooks || (tView.viewHooks = [])).push(getInitFlags(i), def.afterViewInit); + (tView.viewHooks || (tView.viewHooks = [])).push(i, def.afterViewInit); } if (def.afterViewChecked != null) { - (tView.viewHooks || (tView.viewHooks = [])).push(getCheckFlags(i), def.afterViewChecked); + (tView.viewHooks || (tView.viewHooks = [])).push(i, def.afterViewChecked); + (tView.viewCheckHooks || (tView.viewCheckHooks = [])).push(i, def.afterViewChecked); } } @@ -101,26 +89,14 @@ function queueDestroyHooks(def: DirectiveDef, tView: TView, i: number): voi } } -/** Generates flags for init-only hooks */ -function getInitFlags(index: number): number { - return index << LifecycleHook.INDX_SHIFT; -} - -/** Generates flags for hooks called every change detection run */ -function getCheckFlags(index: number): number { - return (index << LifecycleHook.INDX_SHIFT) | LifecycleHook.ON_CHECK; -} - /** * Calls onInit and doCheck calls if they haven't already been called. * * @param currentView The current view */ -export function executeInitHooks(currentView: LView): void { - const initHooks = currentView.tView.initHooks; - - if (currentView.lifecycleStage === LifecycleStage.INIT && initHooks != null) { - executeLifecycleHooks(currentView, initHooks); +export function executeInitHooks(currentView: LView, tView: TView, creationMode: boolean): void { + if (currentView.lifecycleStage === LifecycleStage.INIT) { + executeHooks(currentView.data, tView.initHooks, tView.checkHooks, creationMode); currentView.lifecycleStage = LifecycleStage.CONTENT_INIT; } } @@ -131,11 +107,9 @@ export function executeInitHooks(currentView: LView): void { * * @param currentView The current view */ -export function executeContentHooks(currentView: LView): void { - const contentHooks = currentView.tView.contentHooks; - - if (currentView.lifecycleStage < LifecycleStage.VIEW_INIT && contentHooks != null) { - executeLifecycleHooks(currentView, contentHooks); +export function executeContentHooks(currentView: LView, tView: TView, creationMode: boolean): void { + if (currentView.lifecycleStage < LifecycleStage.VIEW_INIT) { + executeHooks(currentView.data, tView.contentHooks, tView.contentCheckHooks, creationMode); currentView.lifecycleStage = LifecycleStage.VIEW_INIT; } } @@ -145,11 +119,12 @@ export function executeContentHooks(currentView: LView): void { * * @param currentView The current view */ -export function executeViewHooks(currentView: LView): void { - const viewHooks = currentView.tView.viewHooks; - - if (viewHooks != null) { - executeLifecycleHooks(currentView, viewHooks); +export function executeHooks( + data: any[], allHooks: HookData | null, checkHooks: HookData | null, + creationMode: boolean): void { + const hooksToCall = creationMode ? allHooks : checkHooks; + if (hooksToCall != null) { + callHooks(data, hooksToCall); } } @@ -160,15 +135,8 @@ export function executeViewHooks(currentView: LView): void { * @param currentView The current view * @param arr The array in which the hooks are found */ -function executeLifecycleHooks(currentView: LView, arr: HookData): void { - const data = currentView.data; - const creationMode = currentView.creationMode; - +export function callHooks(data: any[], arr: HookData): void { for (let i = 0; i < arr.length; i += 2) { - const flags = arr[i] as number; - const initOnly = (flags & LifecycleHook.TYPE_MASK) === LifecycleHook.ON_INIT; - if (initOnly === false || creationMode) { - (arr[i | 1] as() => void).call(data[flags >> LifecycleHook.INDX_SHIFT]); - } + (arr[i | 1] as() => void).call(data[arr[i] as number]); } } diff --git a/packages/core/src/render3/index.ts b/packages/core/src/render3/index.ts index af03107749..11c3200fc5 100644 --- a/packages/core/src/render3/index.ts +++ b/packages/core/src/render3/index.ts @@ -70,8 +70,6 @@ export { queryRefresh as qR, } from './query'; -export {LifecycleHook} from './hooks'; - // clang-format on export { diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index a156f6fcce..149ae89e6b 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -21,7 +21,7 @@ import {isNodeMatchingSelector} from './node_selector_matcher'; import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveType} from './interfaces/definition'; import {RElement, RText, Renderer3, RendererFactory3, ProceduralRenderer3, ObjectOrientedRenderer3, RendererStyleFlags3} from './interfaces/renderer'; import {isDifferent, stringify} from './util'; -import {executeViewHooks, executeContentHooks, queueLifecycleHooks, queueInitHooks, executeInitHooks} from './hooks'; +import {executeHooks, executeContentHooks, queueLifecycleHooks, queueInitHooks, executeInitHooks} from './hooks'; /** @@ -151,7 +151,9 @@ export function enterView(newView: LView, host: LElementNode | LViewNode | null) * the direction of traversal (up or down the view tree) a bit clearer. */ export function leaveView(newView: LView): void { - executeViewHooks(currentView); + executeHooks( + currentView.data, currentView.tView.viewHooks, currentView.tView.viewCheckHooks, + creationMode); currentView.creationMode = false; currentView.lifecycleStage = LifecycleStage.INIT; currentView.tView.firstTemplatePass = false; @@ -486,8 +488,11 @@ export function createTView(): TView { data: [], firstTemplatePass: true, initHooks: null, + checkHooks: null, contentHooks: null, + contentCheckHooks: null, viewHooks: null, + viewCheckHooks: null, destroyHooks: null }; } @@ -1044,7 +1049,7 @@ export function containerRefreshStart(index: number): void { // We need to execute init hooks here so ngOnInit hooks are called in top level views // before they are called in embedded views (for backwards compatibility). - executeInitHooks(currentView); + executeInitHooks(currentView, currentView.tView, creationMode); } /** @@ -1175,8 +1180,8 @@ export function viewEnd(): void { * @param elementIndex */ export function componentRefresh(directiveIndex: number, elementIndex: number): void { - executeInitHooks(currentView); - executeContentHooks(currentView); + executeInitHooks(currentView, currentView.tView, creationMode); + executeContentHooks(currentView, currentView.tView, creationMode); const template = (tData[directiveIndex] as ComponentDef).template; if (template != null) { ngDevMode && assertDataInRange(elementIndex); diff --git a/packages/core/src/render3/interfaces/view.ts b/packages/core/src/render3/interfaces/view.ts index 3dcc56def9..2cba73935e 100644 --- a/packages/core/src/render3/interfaces/view.ts +++ b/packages/core/src/render3/interfaces/view.ts @@ -200,31 +200,58 @@ export interface TView { firstTemplatePass: boolean; /** - * Array of ngOnInit and ngDoCheck hooks that should be executed for this view. + * Array of ngOnInit and ngDoCheck hooks that should be executed for this view in + * creation mode. * - * Even indices: Flags (1st bit: hook type, remaining: directive index) + * Even indices: Directive index * Odd indices: Hook function */ initHooks: HookData|null; /** - * Array of ngAfterContentInit and ngAfterContentChecked hooks that should be executed for - * this view. + * Array of ngDoCheck hooks that should be executed for this view in update mode. * - * Even indices: Flags (1st bit: hook type, remaining: directive index) + * Even indices: Directive index + * Odd indices: Hook function + */ + checkHooks: HookData|null; + + /** + * Array of ngAfterContentInit and ngAfterContentChecked hooks that should be executed + * for this view in creation mode. + * + * Even indices: Directive index * Odd indices: Hook function */ contentHooks: HookData|null; /** - * Array of ngAfterViewInit and ngAfterViewChecked hooks that should be executed for - * this view. + * Array of ngAfterContentChecked hooks that should be executed for this view in update + * mode. * - * Even indices: Flags (1st bit: hook type, remaining: directive index) + * Even indices: Directive index + * Odd indices: Hook function + */ + contentCheckHooks: HookData|null; + + /** + * Array of ngAfterViewInit and ngAfterViewChecked hooks that should be executed for + * this view in creation mode. + * + * Even indices: Directive index * Odd indices: Hook function */ viewHooks: HookData|null; + /** + * Array of ngAfterViewChecked hooks that should be executed for this view in + * update mode. + * + * Even indices: Directive index + * Odd indices: Hook function + */ + viewCheckHooks: HookData|null; + /** * Array of ngOnDestroy hooks that should be executed when this view is destroyed. * @@ -237,7 +264,7 @@ export interface TView { /** * Array of hooks that should be executed for a view and their directive indices. * - * Even indices: Flags (1st bit: hook type, remaining: directive index) + * Even indices: Directive index * Odd indices: Hook function */ export type HookData = (number | (() => void))[]; diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts index d276815e07..427a898c10 100644 --- a/packages/core/src/render3/node_manipulation.ts +++ b/packages/core/src/render3/node_manipulation.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {assertNotEqual, assertNotNull} from './assert'; +import {assertNotNull} from './assert'; +import {callHooks} from './hooks'; import {LContainer, unusedValueExportToPlacateAjd as unused1} from './interfaces/container'; import {LContainerNode, LElementNode, LNode, LNodeFlags, LProjectionNode, LTextNode, LViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node'; import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection'; @@ -385,10 +386,7 @@ function executeOnDestroys(view: LView): void { const tView = view.tView; let destroyHooks: HookData|null; if (tView != null && (destroyHooks = tView.destroyHooks) != null) { - for (let i = 0; i < destroyHooks.length; i += 2) { - const instance = view.data[destroyHooks[i] as number]; - (destroyHooks[i | 1] as() => void).call(instance); - } + callHooks(view.data, destroyHooks); } }