parent
98174758ad
commit
9c99e6a838
|
@ -13,8 +13,7 @@ import {ComponentRef as viewEngine_ComponentRef} from '../linker/component_facto
|
|||
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef} from '../linker/view_ref';
|
||||
|
||||
import {assertNotNull} from './assert';
|
||||
|
||||
import {NG_HOST_SYMBOL, createError, createLView, directiveCreate, enterView, hostElement, leaveView, locateHostElement, renderComponentOrTemplate} from './instructions';
|
||||
import {NG_HOST_SYMBOL, createError, createLView, createTView, directiveCreate, enterView, hostElement, leaveView, locateHostElement, renderComponentOrTemplate} from './instructions';
|
||||
import {ComponentDef, ComponentType} from './interfaces/definition';
|
||||
import {LElementNode} from './interfaces/node';
|
||||
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
|
||||
|
@ -173,7 +172,7 @@ export function renderComponent<T>(
|
|||
const hostNode = locateHostElement(rendererFactory, opts.host || componentDef.tag);
|
||||
const oldView = enterView(
|
||||
createLView(
|
||||
-1, rendererFactory.createRenderer(hostNode, componentDef.rendererType), {data: []}),
|
||||
-1, rendererFactory.createRenderer(hostNode, componentDef.rendererType), createTView()),
|
||||
null !);
|
||||
try {
|
||||
// Create element node at index 0 in data array
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
|
||||
import {LifecycleHooksMap} from './interfaces/definition';
|
||||
import {LNodeFlags} from './interfaces/node';
|
||||
import {HookData, LView, TView} from './interfaces/view';
|
||||
|
||||
|
||||
/**
|
||||
* Enum used by the lifecycle (l) instruction to determine which lifecycle hook is requesting
|
||||
* processing.
|
||||
*/
|
||||
export const enum LifecycleHook {
|
||||
ON_INIT = 0b00,
|
||||
ON_CHECK = 0b01,
|
||||
ON_CHANGES = 0b10
|
||||
}
|
||||
|
||||
/** Constants used by lifecycle hooks to determine when and how a hook should be called. */
|
||||
export const enum LifecycleHookUtils {
|
||||
/* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops through the directives on a node and queues their afterContentInit,
|
||||
* afterContentChecked, and onDestroy hooks, if they exist.
|
||||
*/
|
||||
export function queueLifecycleHooks(flags: number, currentView: LView): void {
|
||||
// It's necessary to loop through the directives at elementEnd() (rather than storing
|
||||
// the hooks at creation time) so we can preserve the current hook order. All hooks
|
||||
// for projected components and directives must be called *before* their hosts.
|
||||
const size = (flags & LNodeFlags.SIZE_MASK) >> LNodeFlags.SIZE_SHIFT;
|
||||
const start = flags >> LNodeFlags.INDX_SHIFT;
|
||||
let contentHooks = currentView.contentHooks;
|
||||
let cleanup = currentView.cleanup;
|
||||
|
||||
for (let i = start, end = start + size; i < end; i++) {
|
||||
const instance = currentView.data[i];
|
||||
if (instance.ngAfterContentInit != null) {
|
||||
(contentHooks || (currentView.contentHooks = contentHooks = [
|
||||
])).push(LifecycleHook.ON_INIT, instance.ngAfterContentInit, instance);
|
||||
}
|
||||
if (instance.ngAfterContentChecked != null) {
|
||||
(contentHooks || (currentView.contentHooks = contentHooks = [
|
||||
])).push(LifecycleHook.ON_CHECK, instance.ngAfterContentChecked, instance);
|
||||
}
|
||||
if (instance.ngOnDestroy != null) {
|
||||
(cleanup || (currentView.cleanup = cleanup = [])).push(instance.ngOnDestroy, instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is the first template pass, any ngOnInit or ngDoCheck hooks on the current directive
|
||||
* will be queued on TView.initHooks.
|
||||
*
|
||||
* The directive index and hook type are encoded into one number (1st bit: type, remaining bits:
|
||||
* directive index), then saved in the even indices of the initHooks array. The odd indices
|
||||
* hold the hook functions themselves.
|
||||
*
|
||||
* @param index The index of the directive in LView.data
|
||||
* @param hooks The static hooks map on the directive def
|
||||
* @param tView The current TView
|
||||
*/
|
||||
export function queueInitHooks(index: number, hooks: LifecycleHooksMap, tView: TView): void {
|
||||
if (tView.firstTemplatePass && hooks.onInit != null) {
|
||||
const hookFlags = index << LifecycleHookUtils.INDX_SHIFT;
|
||||
(tView.initHooks || (tView.initHooks = [])).push(hookFlags, hooks.onInit);
|
||||
}
|
||||
|
||||
if (tView.firstTemplatePass && hooks.doCheck != null) {
|
||||
const hookFlags = (index << LifecycleHookUtils.INDX_SHIFT) | LifecycleHook.ON_CHECK;
|
||||
(tView.initHooks || (tView.initHooks = [])).push(hookFlags, hooks.doCheck);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls onInit and doCheck calls if they haven't already been called.
|
||||
*
|
||||
* @param currentView The current view
|
||||
* @param initHooks The init hooks for this view
|
||||
*/
|
||||
export function executeInitHooks(currentView: LView): void {
|
||||
const initHooks = currentView.tView.initHooks;
|
||||
|
||||
if (currentView.initHooksCalled === false && initHooks != null) {
|
||||
const data = currentView.data;
|
||||
const creationMode = currentView.creationMode;
|
||||
|
||||
for (let i = 0; i < initHooks.length; i += 2) {
|
||||
const flags = initHooks[i] as number;
|
||||
const hook = initHooks[i | 1] as() => void;
|
||||
const onInit = (flags & LifecycleHookUtils.TYPE_MASK) === LifecycleHook.ON_INIT;
|
||||
const instance = data[flags >> LifecycleHookUtils.INDX_SHIFT];
|
||||
if (onInit === false || creationMode) {
|
||||
hook.call(instance);
|
||||
}
|
||||
}
|
||||
currentView.initHooksCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Iterates over view hook functions and calls them. */
|
||||
export function executeViewHooks(data: any[], viewHookStartIndex: number | null): void {
|
||||
if (viewHookStartIndex == null) return;
|
||||
executeHooksAndRemoveInits(data, viewHookStartIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls all afterContentInit and afterContentChecked hooks for the view, then splices
|
||||
* out afterContentInit hooks to prep for the next run in update mode.
|
||||
*/
|
||||
export function executeContentHooks(currentView: LView): void {
|
||||
if (currentView.contentHooks != null && currentView.contentHooksCalled === false) {
|
||||
executeHooksAndRemoveInits(currentView.contentHooks, 0);
|
||||
currentView.contentHooksCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls lifecycle hooks with their contexts, then splices out any init-only hooks
|
||||
* to prep for the next run in update mode.
|
||||
*
|
||||
* @param arr The array in which the hooks are found
|
||||
* @param startIndex The index at which to start calling hooks
|
||||
*/
|
||||
function executeHooksAndRemoveInits(arr: any[], startIndex: number): void {
|
||||
// Instead of using splice to remove init hooks after their first run (expensive), we
|
||||
// shift over the AFTER_CHECKED hooks as we call them and truncate once at the end.
|
||||
let checkIndex = startIndex;
|
||||
let writeIndex = startIndex;
|
||||
while (checkIndex < arr.length) {
|
||||
// Call lifecycle hook with its context
|
||||
arr[checkIndex + 1].call(arr[checkIndex + 2]);
|
||||
|
||||
if (arr[checkIndex] === LifecycleHook.ON_CHECK) {
|
||||
// We know if the writeIndex falls behind that there is an init that needs to
|
||||
// be overwritten.
|
||||
if (writeIndex < checkIndex) {
|
||||
arr[writeIndex] = arr[checkIndex];
|
||||
arr[writeIndex + 1] = arr[checkIndex + 1];
|
||||
arr[writeIndex + 2] = arr[checkIndex + 2];
|
||||
}
|
||||
writeIndex += 3;
|
||||
}
|
||||
checkIndex += 3;
|
||||
}
|
||||
|
||||
// Truncate once at the writeIndex
|
||||
arr.length = writeIndex;
|
||||
}
|
|
@ -24,8 +24,6 @@ export {InjectFlags, QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_REA
|
|||
// clang-format off
|
||||
export {
|
||||
|
||||
LifecycleHook,
|
||||
|
||||
NO_CHANGE as NC,
|
||||
|
||||
bind as b,
|
||||
|
@ -72,6 +70,9 @@ export {
|
|||
query as Q,
|
||||
queryRefresh as qR,
|
||||
} from './query';
|
||||
|
||||
export {LifecycleHook} from './hooks';
|
||||
|
||||
// clang-format on
|
||||
|
||||
export {
|
||||
|
|
|
@ -27,20 +27,9 @@ import {isNodeMatchingSelector} from './node_selector_matcher';
|
|||
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveType} from './interfaces/definition';
|
||||
import {RComment, RElement, RText, Renderer3, RendererFactory3, ProceduralRenderer3, ObjectOrientedRenderer3, RendererStyleFlags3} from './interfaces/renderer';
|
||||
import {isDifferent, stringify} from './util';
|
||||
import {LifecycleHook, executeViewHooks, executeContentHooks, queueLifecycleHooks, queueInitHooks, executeInitHooks} from './hooks';
|
||||
|
||||
|
||||
/**
|
||||
* Enum used by the lifecycle (l) instruction to determine which lifecycle hook is requesting
|
||||
* processing.
|
||||
*/
|
||||
export const enum LifecycleHook {
|
||||
ON_INIT = 1,
|
||||
ON_DESTROY = 2,
|
||||
ON_CHANGES = 4,
|
||||
AFTER_INIT = 8,
|
||||
AFTER_CHECKED = 16
|
||||
}
|
||||
|
||||
/**
|
||||
* Directive (D) sets a property on all component instances using this constant as a key and the
|
||||
* component's host node (LElement) as the value. This is used in methods like detectChanges to
|
||||
|
@ -91,7 +80,7 @@ let tData: TData;
|
|||
/** State of the current view being processed. */
|
||||
let currentView: LView;
|
||||
// The initialization has to be after the `let`, otherwise `createLView` can't see `let`.
|
||||
currentView = createLView(null !, null !, {data: []});
|
||||
currentView = createLView(null !, null !, createTView());
|
||||
|
||||
let currentQuery: LQuery|null;
|
||||
|
||||
|
@ -129,19 +118,6 @@ let bindingIndex: number;
|
|||
*/
|
||||
let cleanup: any[]|null;
|
||||
|
||||
/**
|
||||
* Array of ngAfterContentInit and ngAfterContentChecked hooks.
|
||||
*
|
||||
* These need to be queued so they can be called all at once after init hooks
|
||||
* and any embedded views are finished processing (to maintain backwards-compatible
|
||||
* order).
|
||||
*
|
||||
* 1st index is: type of hook (afterContentInit or afterContentChecked)
|
||||
* 2nd index is: method to call
|
||||
* 3rd index is: context
|
||||
*/
|
||||
let contentHooks: any[]|null;
|
||||
|
||||
/** Index in the data array at which view hooks begin to be stored. */
|
||||
let viewHookStartIndex: number|null;
|
||||
|
||||
|
@ -166,7 +142,6 @@ export function enterView(newView: LView, host: LElementNode | LViewNode | null)
|
|||
|
||||
viewHookStartIndex = newView.viewHookStartIndex;
|
||||
cleanup = newView.cleanup;
|
||||
contentHooks = newView.contentHooks;
|
||||
renderer = newView.renderer;
|
||||
|
||||
if (host != null) {
|
||||
|
@ -183,8 +158,10 @@ 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();
|
||||
executeViewHooks(data, viewHookStartIndex);
|
||||
currentView.initHooksCalled = false;
|
||||
currentView.contentHooksCalled = false;
|
||||
if (currentView.tView.firstTemplatePass) currentView.tView.firstTemplatePass = false;
|
||||
enterView(newView, null);
|
||||
}
|
||||
|
||||
|
@ -209,7 +186,8 @@ export function createLView(
|
|||
template: template,
|
||||
context: context,
|
||||
dynamicViewCount: 0,
|
||||
contentHooksCalled: false
|
||||
contentHooksCalled: false,
|
||||
initHooksCalled: false
|
||||
};
|
||||
|
||||
return newView;
|
||||
|
@ -508,7 +486,11 @@ function hack_findQueryName(
|
|||
* @returns TView
|
||||
*/
|
||||
function getOrCreateTView(template: ComponentTemplate<any>): TView {
|
||||
return template.ngPrivateData || (template.ngPrivateData = { data: [] } as never);
|
||||
return template.ngPrivateData || (template.ngPrivateData = createTView() as never);
|
||||
}
|
||||
|
||||
export function createTView(): TView {
|
||||
return {data: [], firstTemplatePass: true, initHooks: null};
|
||||
}
|
||||
|
||||
function setUpAttributes(native: RElement, attrs: string[]): void {
|
||||
|
@ -627,35 +609,7 @@ export function elementEnd() {
|
|||
ngDevMode && assertNodeType(previousOrParentNode, LNodeFlags.Element);
|
||||
const query = previousOrParentNode.query;
|
||||
query && query.addNode(previousOrParentNode);
|
||||
queueLifecycleHooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops through the directives on a node and queues their afterContentInit,
|
||||
* afterContentChecked, and onDestroy hooks, if they exist.
|
||||
*/
|
||||
function queueLifecycleHooks(): void {
|
||||
// It's necessary to loop through the directives at elementEnd() (rather than storing
|
||||
// the hooks at creation time) so we can preserve the current hook order. All hooks
|
||||
// for projected components and directives must be called *before* their hosts.
|
||||
const flags = previousOrParentNode.flags;
|
||||
const size = (flags & LNodeFlags.SIZE_MASK) >> LNodeFlags.SIZE_SHIFT;
|
||||
const start = flags >> LNodeFlags.INDX_SHIFT;
|
||||
|
||||
for (let i = start, end = start + size; i < end; i++) {
|
||||
const instance = data[i];
|
||||
if (instance.ngAfterContentInit != null) {
|
||||
(contentHooks || (currentView.contentHooks = contentHooks = [
|
||||
])).push(LifecycleHook.AFTER_INIT, instance.ngAfterContentInit, instance);
|
||||
}
|
||||
if (instance.ngAfterContentChecked != null) {
|
||||
(contentHooks || (currentView.contentHooks = contentHooks = [
|
||||
])).push(LifecycleHook.AFTER_CHECKED, instance.ngAfterContentChecked, instance);
|
||||
}
|
||||
if (instance.ngOnDestroy != null) {
|
||||
(cleanup || (currentView.cleanup = cleanup = [])).push(instance.ngOnDestroy, instance);
|
||||
}
|
||||
}
|
||||
queueLifecycleHooks(previousOrParentNode.flags, currentView);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -949,6 +903,11 @@ export function directiveCreate<T>(
|
|||
if (tNode && tNode.attrs) {
|
||||
setInputsFromAttrs<T>(instance, directiveDef !.inputs, tNode);
|
||||
}
|
||||
|
||||
// Init hooks are queued now so ngOnInit is called in host components before
|
||||
// any projected components.
|
||||
queueInitHooks(index, directiveDef.lifecycleHooks, currentView.tView);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
@ -1013,31 +972,13 @@ function generateInitialInputs(
|
|||
* Accepts a lifecycle hook type and determines when and how the related lifecycle hook
|
||||
* callback should run.
|
||||
*
|
||||
* For the onInit lifecycle hook, it will return whether or not the ngOnInit() function
|
||||
* should run. If so, ngOnInit() will be called outside of this function.
|
||||
*
|
||||
* e.g. l(LifecycleHook.ON_INIT) && ctx.ngOnInit();
|
||||
*
|
||||
* For the onDestroy lifecycle hook, this instruction also accepts an onDestroy
|
||||
* method that should be stored and called internally when the parent view is being
|
||||
* cleaned up.
|
||||
*
|
||||
* e.g. l(LifecycleHook.ON_DESTROY, ctx, ctx.onDestroy);
|
||||
*
|
||||
* @param lifecycle
|
||||
* @param self
|
||||
* @param method
|
||||
*/
|
||||
export function lifecycle(lifecycle: LifecycleHook.AFTER_INIT, self: any, method: Function): void;
|
||||
export function lifecycle(
|
||||
lifecycle: LifecycleHook.AFTER_CHECKED, self: any, method: Function): void;
|
||||
export function lifecycle(lifecycle: LifecycleHook): boolean;
|
||||
export function lifecycle(lifecycle: LifecycleHook, self?: any, method?: Function): boolean {
|
||||
if (lifecycle === LifecycleHook.ON_INIT) {
|
||||
return creationMode;
|
||||
} else if (
|
||||
creationMode &&
|
||||
(lifecycle === LifecycleHook.AFTER_INIT || lifecycle === LifecycleHook.AFTER_CHECKED)) {
|
||||
export function lifecycle(lifecycle: LifecycleHook, self: any, method: Function): boolean {
|
||||
if (creationMode &&
|
||||
(lifecycle === LifecycleHook.ON_INIT || lifecycle === LifecycleHook.ON_CHECK)) {
|
||||
if (viewHookStartIndex == null) {
|
||||
currentView.viewHookStartIndex = viewHookStartIndex = data.length;
|
||||
}
|
||||
|
@ -1046,12 +987,6 @@ export function lifecycle(lifecycle: LifecycleHook, self?: any, method?: Functio
|
|||
return false;
|
||||
}
|
||||
|
||||
/** Iterates over view hook functions and calls them. */
|
||||
export function executeViewHooks(): void {
|
||||
if (viewHookStartIndex == null) return;
|
||||
executeHooksAndRemoveInits(data, viewHookStartIndex);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////
|
||||
//// ViewContainer & View
|
||||
|
@ -1124,6 +1059,10 @@ export function containerRefreshStart(index: number): void {
|
|||
ngDevMode && assertNodeType(previousOrParentNode, LNodeFlags.Container);
|
||||
isParent = true;
|
||||
(previousOrParentNode as LContainerNode).data.nextIndex = 0;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1210,7 +1149,7 @@ function getOrCreateEmbeddedTView(viewIndex: number, parent: LContainerNode): TV
|
|||
ngDevMode && assertNodeType(parent, LNodeFlags.Container);
|
||||
const tContainer = (parent !.tNode as TContainerNode).data;
|
||||
if (viewIndex >= tContainer.length || tContainer[viewIndex] == null) {
|
||||
tContainer[viewIndex] = { data: [] } as TView;
|
||||
tContainer[viewIndex] = createTView();
|
||||
}
|
||||
return tContainer[viewIndex];
|
||||
}
|
||||
|
@ -1261,7 +1200,8 @@ export const componentRefresh:
|
|||
ngDevMode && assertDataInRange(directiveIndex);
|
||||
const hostView = element.data !;
|
||||
ngDevMode && assertNotEqual(hostView, null, 'hostView');
|
||||
executeContentHooks();
|
||||
executeInitHooks(currentView);
|
||||
executeContentHooks(currentView);
|
||||
const directive = data[directiveIndex];
|
||||
const oldView = enterView(hostView, element);
|
||||
try {
|
||||
|
@ -1273,49 +1213,6 @@ export const componentRefresh:
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Calls all afterContentInit and afterContentChecked hooks for the view, then splices
|
||||
* out afterContentInit hooks to prep for the next run in update mode.
|
||||
*/
|
||||
function executeContentHooks(): void {
|
||||
if (contentHooks == null || currentView.contentHooksCalled) return;
|
||||
executeHooksAndRemoveInits(contentHooks, 0);
|
||||
currentView.contentHooksCalled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls lifecycle hooks with their contexts, then splices out any init-only hooks
|
||||
* to prep for the next run in update mode.
|
||||
*
|
||||
* @param arr The array in which the hooks are found
|
||||
* @param startIndex The index at which to start calling hooks
|
||||
*/
|
||||
function executeHooksAndRemoveInits(arr: any[], startIndex: number): void {
|
||||
// Instead of using splice to remove init hooks after their first run (expensive), we
|
||||
// shift over the AFTER_CHECKED hooks as we call them and truncate once at the end.
|
||||
let checkIndex = startIndex;
|
||||
let writeIndex = startIndex;
|
||||
while (checkIndex < arr.length) {
|
||||
// Call lifecycle hook with its context
|
||||
arr[checkIndex + 1].call(arr[checkIndex + 2]);
|
||||
|
||||
if (arr[checkIndex] === LifecycleHook.AFTER_CHECKED) {
|
||||
// We know if the writeIndex falls behind that there is an init that needs to
|
||||
// be overwritten.
|
||||
if (writeIndex < checkIndex) {
|
||||
arr[writeIndex] = arr[checkIndex];
|
||||
arr[writeIndex + 1] = arr[checkIndex + 1];
|
||||
arr[writeIndex + 2] = arr[checkIndex + 2];
|
||||
}
|
||||
writeIndex += 3;
|
||||
}
|
||||
checkIndex += 3;
|
||||
}
|
||||
|
||||
// Truncate once at the writeIndex
|
||||
arr.length = writeIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instruction to distribute projectable nodes among <ng-content> occurrences in a given template.
|
||||
* It takes all the selectors from the entire component's template and decides where
|
||||
|
|
|
@ -103,6 +103,16 @@ export interface LView {
|
|||
*/
|
||||
contentHooks: any[]|null;
|
||||
|
||||
/**
|
||||
* Whether or not the ngOnInit and ngDoCheck hooks have been called in this change
|
||||
* detection run.
|
||||
*
|
||||
* These two hooks are executed by the first Comp.r() instruction that runs OR the
|
||||
* first cR instruction that runs (so inits are run for the top level view before
|
||||
* any embedded views). For this reason, the call must be tracked.
|
||||
*/
|
||||
initHooksCalled: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not the content hooks have been called in this change detection run.
|
||||
*
|
||||
|
@ -194,7 +204,29 @@ export interface LViewOrLContainer {
|
|||
*
|
||||
* Stored on the template function as ngPrivateData.
|
||||
*/
|
||||
export interface TView { data: TData; }
|
||||
export interface TView {
|
||||
/** Static data equivalent of LView.data[]. Contains TNodes and directive defs. */
|
||||
data: TData;
|
||||
|
||||
/** Whether or not this template has been processed. */
|
||||
firstTemplatePass: boolean;
|
||||
|
||||
/**
|
||||
* Array of init hooks that should be executed for this view.
|
||||
*
|
||||
* Even indices: Flags (1st bit: hook type, remaining: directive index)
|
||||
* Odd indices: Hook function
|
||||
*/
|
||||
initHooks: HookData|null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Array of init hooks that should be executed for a view.
|
||||
*
|
||||
* Even indices: Flags (1st bit: hook type, remaining: directive index)
|
||||
* Odd indices: Hook function
|
||||
*/
|
||||
export type HookData = (number | (() => void))[];
|
||||
|
||||
/**
|
||||
* Static data that corresponds to the instance-specific data array on an LView.
|
||||
|
|
|
@ -11,7 +11,7 @@ import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
|
|||
import {defineComponent} from '../../src/render3/definition';
|
||||
import {InjectFlags, bloomAdd, bloomFindPossibleInjector, getOrCreateNodeInjector} from '../../src/render3/di';
|
||||
import {C, E, PublicFeature, T, V, b, b2, cR, cr, defineDirective, e, inject, injectElementRef, injectTemplateRef, injectViewContainerRef, m, t, v} from '../../src/render3/index';
|
||||
import {createLNode, createLView, enterView, leaveView} from '../../src/render3/instructions';
|
||||
import {createLNode, createLView, createTView, enterView, leaveView} from '../../src/render3/instructions';
|
||||
import {LInjector} from '../../src/render3/interfaces/injector';
|
||||
import {LNodeFlags} from '../../src/render3/interfaces/node';
|
||||
|
||||
|
@ -312,7 +312,7 @@ describe('di', () => {
|
|||
|
||||
describe('getOrCreateNodeInjector', () => {
|
||||
it('should handle initial undefined state', () => {
|
||||
const contentView = createLView(-1, null !, {data: []});
|
||||
const contentView = createLView(-1, null !, createTView());
|
||||
const oldView = enterView(contentView, null !);
|
||||
try {
|
||||
const parent = createLNode(0, LNodeFlags.Element, null, null);
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {C, ComponentDef, ComponentTemplate, E, L, LifecycleHook, T, V, b, cR, cr, defineComponent, e, l, m, p, r, v, pD, P} from '../../src/render3/index';
|
||||
import {C, ComponentDef, ComponentTemplate, E, L, LifecycleHook, P, T, V, b, cR, cr, defineComponent, defineDirective, e, l, m, p, pD, r, v} from '../../src/render3/index';
|
||||
|
||||
import {containerEl, renderToHtml} from './render_util';
|
||||
|
||||
describe('lifecycles', () => {
|
||||
|
@ -52,8 +53,6 @@ describe('lifecycles', () => {
|
|||
type: Component,
|
||||
tag: name,
|
||||
factory: () => new Component(),
|
||||
hostBindings: function(directiveIndex: number, elementIndex: number):
|
||||
void { l(LifecycleHook.ON_INIT) && m<Component>(directiveIndex).ngOnInit(); },
|
||||
inputs: {val: 'val'}, template
|
||||
});
|
||||
};
|
||||
|
@ -220,6 +219,30 @@ describe('lifecycles', () => {
|
|||
expect(events).toEqual(['comp1', 'projected1', 'comp2', 'projected2']);
|
||||
});
|
||||
|
||||
it('should be called on directives after component', () => {
|
||||
class Directive {
|
||||
ngOnInit() { events.push('dir'); }
|
||||
|
||||
static ngDirectiveDef = defineDirective({type: Directive, factory: () => new Directive()});
|
||||
}
|
||||
|
||||
function Template(ctx: any, cm: boolean) {
|
||||
if (cm) {
|
||||
E(0, Comp, null, [Directive]);
|
||||
e();
|
||||
}
|
||||
Comp.ngComponentDef.h(1, 0);
|
||||
Comp.ngComponentDef.r(1, 0);
|
||||
}
|
||||
|
||||
renderToHtml(Template, {});
|
||||
expect(events).toEqual(['comp', 'dir']);
|
||||
|
||||
renderToHtml(Template, {});
|
||||
expect(events).toEqual(['comp', 'dir']);
|
||||
|
||||
});
|
||||
|
||||
it('should call onInit properly in for loop', () => {
|
||||
/**
|
||||
* <comp [val]="1"></comp>
|
||||
|
@ -334,22 +357,13 @@ describe('lifecycles', () => {
|
|||
return class Component {
|
||||
ngDoCheck() {
|
||||
events.push(name);
|
||||
allEvents.push('ngDoCheck ' + name);
|
||||
allEvents.push('check ' + name);
|
||||
}
|
||||
|
||||
ngOnInit() { allEvents.push('ngOnInit ' + name); }
|
||||
ngOnInit() { allEvents.push('init ' + name); }
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
type: Component,
|
||||
tag: name,
|
||||
factory: () => new Component(),
|
||||
hostBindings: function(
|
||||
this: ComponentDef<Component>, directiveIndex: number, elementIndex: number): void {
|
||||
l(LifecycleHook.ON_INIT) && m<Component>(directiveIndex).ngOnInit();
|
||||
m<Component>(directiveIndex).ngDoCheck();
|
||||
},
|
||||
template
|
||||
});
|
||||
static ngComponentDef =
|
||||
defineComponent({type: Component, tag: name, factory: () => new Component(), template});
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -402,10 +416,10 @@ describe('lifecycles', () => {
|
|||
}
|
||||
|
||||
renderToHtml(Template, {});
|
||||
expect(allEvents).toEqual(['ngOnInit comp', 'ngDoCheck comp']);
|
||||
expect(allEvents).toEqual(['init comp', 'check comp']);
|
||||
|
||||
renderToHtml(Template, {});
|
||||
expect(allEvents).toEqual(['ngOnInit comp', 'ngDoCheck comp', 'ngDoCheck comp']);
|
||||
expect(allEvents).toEqual(['init comp', 'check comp', 'check comp']);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -829,8 +843,8 @@ describe('lifecycles', () => {
|
|||
refresh: (directiveIndex: number, elementIndex: number) => {
|
||||
r(directiveIndex, elementIndex, template);
|
||||
const comp = m(directiveIndex) as Component;
|
||||
l(LifecycleHook.AFTER_INIT, comp, comp.ngAfterViewInit);
|
||||
l(LifecycleHook.AFTER_CHECKED, comp, comp.ngAfterViewChecked);
|
||||
l(LifecycleHook.ON_INIT, comp, comp.ngAfterViewInit);
|
||||
l(LifecycleHook.ON_CHECK, comp, comp.ngAfterViewChecked);
|
||||
},
|
||||
inputs: {val: 'val'},
|
||||
template: template
|
||||
|
@ -1693,16 +1707,11 @@ describe('lifecycles', () => {
|
|||
type: Component,
|
||||
tag: name,
|
||||
factory: () => new Component(),
|
||||
hostBindings: function(directiveIndex: number, elementIndex: number): void {
|
||||
const comp = D(directiveIndex) as Component;
|
||||
l(LifecycleHook.ON_INIT) && comp.ngOnInit();
|
||||
comp.ngDoCheck();
|
||||
},
|
||||
refresh: function(directiveIndex: number, elementIndex: number): void {
|
||||
r(directiveIndex, elementIndex, template);
|
||||
const comp = D(directiveIndex) as Component;
|
||||
l(LifecycleHook.AFTER_INIT, comp, comp.ngAfterViewInit);
|
||||
l(LifecycleHook.AFTER_CHECKED, comp, comp.ngAfterViewChecked);
|
||||
const comp = m(directiveIndex) as Component;
|
||||
l(LifecycleHook.ON_INIT, comp, comp.ngAfterViewInit);
|
||||
l(LifecycleHook.ON_CHECK, comp, comp.ngAfterViewChecked);
|
||||
},
|
||||
inputs: {val: 'val'}, template
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue