fix(ivy): support property values changed in ngOnChanges (forward rref case) (#29054)

PR Close #29054
This commit is contained in:
Marc Laval
2019-03-01 14:39:28 +01:00
committed by Andrew Kushnir
parent 6215799055
commit 25166d4f41
24 changed files with 609 additions and 114 deletions

View File

@ -104,6 +104,7 @@ export {
elementStyleProp as ɵelementStyleProp,
elementStylingApply as ɵelementStylingApply,
elementClassProp as ɵelementClassProp,
flushHooksUpTo as ɵflushHooksUpTo,
textBinding as ɵtextBinding,
template as ɵtemplate,
embeddedViewEnd as ɵembeddedViewEnd,

View File

@ -29,7 +29,7 @@ import {renderInitialClasses, renderInitialStyles} from './styling/class_and_sty
import {publishDefaultGlobalUtils} from './util/global_utils';
import {defaultScheduler, renderStringify} from './util/misc_utils';
import {getRootContext, getRootView} from './util/view_traversal_utils';
import {readPatchedLView} from './util/view_utils';
import {readPatchedLView, resetPreOrderHookFlags} from './util/view_utils';
@ -142,6 +142,7 @@ export function renderComponent<T>(
refreshDescendantViews(rootView); // creation mode pass
rootView[FLAGS] &= ~LViewFlags.CreationMode;
resetPreOrderHookFlags(rootView);
refreshDescendantViews(rootView); // update mode pass
} finally {
leaveView(oldView);
@ -248,7 +249,7 @@ export function LifecycleHooksFeature(component: any, def: ComponentDef<any>): v
const rootTView = readPatchedLView(component) ![TVIEW];
const dirIndex = rootTView.data.length - 1;
registerPreOrderHooks(dirIndex, def, rootTView);
registerPreOrderHooks(dirIndex, def, rootTView, -1, -1, -1);
// TODO(misko): replace `as TNode` with createTNode call. (needs refactoring to lose dep on
// LNode).
registerPostOrderHooks(

View File

@ -10,7 +10,7 @@ import {assertEqual} from '../util/assert';
import {DirectiveDef} from './interfaces/definition';
import {TNode} from './interfaces/node';
import {FLAGS, HookData, InitPhaseState, LView, LViewFlags, TView} from './interfaces/view';
import {FLAGS, HookData, InitPhaseState, LView, LViewFlags, PREORDER_HOOK_FLAGS, PreOrderHookFlags, TView} from './interfaces/view';
@ -19,34 +19,50 @@ import {FLAGS, HookData, InitPhaseState, LView, LViewFlags, TView} from './inter
*
* Must be run *only* on the first template pass.
*
* The TView's hooks arrays are arranged in alternating pairs of directiveIndex and hookFunction,
* i.e.: `[directiveIndexA, hookFunctionA, directiveIndexB, hookFunctionB, ...]`. For `OnChanges`
* hooks, the `directiveIndex` will be *negative*, signaling {@link callHooks} that the
* `hookFunction` must be passed the the appropriate {@link SimpleChanges} object.
* Sets up the pre-order hooks on the provided `tView`,
* see {@link HookData} for details about the data structure.
*
* @param directiveIndex The index of the directive in LView
* @param directiveDef The definition containing the hooks to setup in tView
* @param tView The current TView
* @param nodeIndex The index of the node to which the directive is attached
* @param initialPreOrderHooksLength the number of pre-order hooks already registered before the
* current process, used to know if the node index has to be added to the array. If it is -1,
* the node index is never added.
* @param initialPreOrderCheckHooksLength same as previous for pre-order check hooks
*/
export function registerPreOrderHooks(
directiveIndex: number, directiveDef: DirectiveDef<any>, tView: TView): void {
directiveIndex: number, directiveDef: DirectiveDef<any>, tView: TView, nodeIndex: number,
initialPreOrderHooksLength: number, initialPreOrderCheckHooksLength: number): void {
ngDevMode &&
assertEqual(tView.firstTemplatePass, true, 'Should only be called on first template pass');
const {onChanges, onInit, doCheck} = directiveDef;
if (initialPreOrderHooksLength >= 0 &&
(!tView.preOrderHooks || initialPreOrderHooksLength === tView.preOrderHooks.length) &&
(onChanges || onInit || doCheck)) {
(tView.preOrderHooks || (tView.preOrderHooks = [])).push(nodeIndex);
}
if (initialPreOrderCheckHooksLength >= 0 &&
(!tView.preOrderCheckHooks ||
initialPreOrderCheckHooksLength === tView.preOrderCheckHooks.length) &&
(onChanges || doCheck)) {
(tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [])).push(nodeIndex);
}
if (onChanges) {
(tView.initHooks || (tView.initHooks = [])).push(directiveIndex, onChanges);
(tView.checkHooks || (tView.checkHooks = [])).push(directiveIndex, onChanges);
(tView.preOrderHooks || (tView.preOrderHooks = [])).push(directiveIndex, onChanges);
(tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [])).push(directiveIndex, onChanges);
}
if (onInit) {
(tView.initHooks || (tView.initHooks = [])).push(-directiveIndex, onInit);
(tView.preOrderHooks || (tView.preOrderHooks = [])).push(-directiveIndex, onInit);
}
if (doCheck) {
(tView.initHooks || (tView.initHooks = [])).push(directiveIndex, doCheck);
(tView.checkHooks || (tView.checkHooks = [])).push(directiveIndex, doCheck);
(tView.preOrderHooks || (tView.preOrderHooks = [])).push(directiveIndex, doCheck);
(tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [])).push(directiveIndex, doCheck);
}
}
@ -59,9 +75,8 @@ export function registerPreOrderHooks(
* preserve hook execution order. Content, view, and destroy hooks for projected
* components and directives must be called *before* their hosts.
*
* Sets up the content, view, and destroy hooks on the provided `tView` such that
* they're added in alternating pairs of directiveIndex and hookFunction,
* i.e.: `[directiveIndexA, hookFunctionA, directiveIndexB, hookFunctionB, ...]`
* Sets up the content, view, and destroy hooks on the provided `tView`,
* see {@link HookData} for details about the data structure.
*
* NOTE: This does not set up `onChanges`, `onInit` or `doCheck`, those are set up
* separately at `elementStart`.
@ -103,25 +118,49 @@ export function registerPostOrderHooks(tView: TView, tNode: TNode): void {
}
}
/**
* Executing hooks requires complex logic as we need to deal with 2 constraints.
*
* 1. Init hooks (ngOnInit, ngAfterContentInit, ngAfterViewInit) must all be executed once and only
* once, across many change detection cycles. This must be true even if some hooks throw, or if
* some recursively trigger a change detection cycle.
* To solve that, it is required to track the state of the execution of these init hooks.
* This is done by storing and maintaining flags in the view: the {@link InitPhaseState},
* and the index within that phase. They can be seen as a cursor in the following structure:
* [[onInit1, onInit2], [afterContentInit1], [afterViewInit1, afterViewInit2, afterViewInit3]]
* They are are stored as flags in LView[FLAGS].
*
* 2. Pre-order hooks can be executed in batches, because of the flushHooksUpTo instruction.
* To be able to pause and resume their execution, we also need some state about the hook's array
* that is being processed:
* - the index of the next hook to be executed
* - the number of init hooks already found in the processed part of the array
* They are are stored as flags in LView[PREORDER_HOOK_FLAGS].
*/
/**
* Executes necessary hooks at the start of executing a template.
*
* Executes hooks that are to be run during the initialization of a directive such
* as `onChanges`, `onInit`, and `doCheck`.
*
* Has the side effect of updating the RunInit flag in `lView` to be `0`, so that
* this isn't run a second time.
*
* @param lView The current view
* @param tView Static data for the view containing the hooks to be executed
* @param checkNoChangesMode Whether or not we're in checkNoChanges mode.
* @param @param currentNodeIndex 2 cases depending the the value:
* - undefined: execute hooks only from the saved index until the end of the array (pre-order case,
* when flushing the remaining hooks)
* - number: execute hooks only from the saved index until that node index exclusive (pre-order
* case, when executing flushHooksUpTo(number))
*/
export function executeInitHooks(
currentView: LView, tView: TView, checkNoChangesMode: boolean): void {
export function executePreOrderHooks(
currentView: LView, tView: TView, checkNoChangesMode: boolean,
currentNodeIndex: number | undefined): void {
if (!checkNoChangesMode) {
executeHooks(
currentView, tView.initHooks, tView.checkHooks, checkNoChangesMode,
InitPhaseState.OnInitHooksToBeRun);
currentView, tView.preOrderHooks, tView.preOrderCheckHooks, checkNoChangesMode,
InitPhaseState.OnInitHooksToBeRun,
currentNodeIndex !== undefined ? currentNodeIndex : null);
}
}
@ -129,24 +168,33 @@ export function executeInitHooks(
* Executes hooks against the given `LView` based off of whether or not
* This is the first pass.
*
* @param lView The view instance data to run the hooks against
* @param currentView The view instance data to run the hooks against
* @param firstPassHooks An array of hooks to run if we're in the first view pass
* @param checkHooks An Array of hooks to run if we're not in the first view pass.
* @param checkNoChangesMode Whether or not we're in no changes mode.
* @param initPhaseState the current state of the init phase
* @param currentNodeIndex 3 cases depending the the value:
* - undefined: all hooks from the array should be executed (post-order case)
* - null: execute hooks only from the saved index until the end of the array (pre-order case, when
* flushing the remaining hooks)
* - number: execute hooks only from the saved index until that node index exclusive (pre-order
* case, when executing flushHooksUpTo(number))
*/
export function executeHooks(
currentView: LView, firstPassHooks: HookData | null, checkHooks: HookData | null,
checkNoChangesMode: boolean, initPhase: number): void {
checkNoChangesMode: boolean, initPhaseState: InitPhaseState,
currentNodeIndex: number | null | undefined): void {
if (checkNoChangesMode) return;
const hooksToCall = (currentView[FLAGS] & LViewFlags.InitPhaseStateMask) === initPhase ?
const hooksToCall = (currentView[FLAGS] & LViewFlags.InitPhaseStateMask) === initPhaseState ?
firstPassHooks :
checkHooks;
if (hooksToCall) {
callHooks(currentView, hooksToCall, initPhase);
callHooks(currentView, hooksToCall, initPhaseState, currentNodeIndex);
}
// The init phase state must be always checked here as it may have been recursively updated
if ((currentView[FLAGS] & LViewFlags.InitPhaseStateMask) === initPhase &&
initPhase !== InitPhaseState.InitPhaseCompleted) {
if (currentNodeIndex == null &&
(currentView[FLAGS] & LViewFlags.InitPhaseStateMask) === initPhaseState &&
initPhaseState !== InitPhaseState.InitPhaseCompleted) {
currentView[FLAGS] &= LViewFlags.IndexWithinInitPhaseReset;
currentView[FLAGS] += LViewFlags.InitPhaseStateIncrementer;
}
@ -158,25 +206,68 @@ export function executeHooks(
*
* @param currentView The current view
* @param arr The array in which the hooks are found
* @param initPhaseState the current state of the init phase
* @param currentNodeIndex 3 cases depending the the value:
* - undefined: all hooks from the array should be executed (post-order case)
* - null: execute hooks only from the saved index until the end of the array (pre-order case, when
* flushing the remaining hooks)
* - number: execute hooks only from the saved index until that node index exclusive (pre-order
* case, when executing flushHooksUpTo(number))
*/
export function callHooks(currentView: LView, arr: HookData, initPhase?: number): void {
let initHooksCount = 0;
for (let i = 0; i < arr.length; i += 2) {
const isInitHook = arr[i] < 0;
const directiveIndex = isInitHook ? -arr[i] : arr[i] as number;
const directive = currentView[directiveIndex];
function callHooks(
currentView: LView, arr: HookData, initPhase: InitPhaseState,
currentNodeIndex: number | null | undefined): void {
const startIndex = currentNodeIndex !== undefined ?
(currentView[PREORDER_HOOK_FLAGS] & PreOrderHookFlags.IndexOfTheNextPreOrderHookMaskMask) :
0;
const nodeIndexLimit = currentNodeIndex != null ? currentNodeIndex : -1;
let lastNodeIndexFound = 0;
for (let i = startIndex; i < arr.length; i++) {
const hook = arr[i + 1] as() => void;
if (isInitHook) {
initHooksCount++;
const indexWithintInitPhase = currentView[FLAGS] >> LViewFlags.IndexWithinInitPhaseShift;
// The init phase state must be always checked here as it may have been recursively updated
if (indexWithintInitPhase < initHooksCount &&
(currentView[FLAGS] & LViewFlags.InitPhaseStateMask) === initPhase) {
currentView[FLAGS] += LViewFlags.IndexWithinInitPhaseIncrementer;
hook.call(directive);
if (typeof hook === 'number') {
lastNodeIndexFound = arr[i] as number;
if (currentNodeIndex != null && lastNodeIndexFound >= currentNodeIndex) {
break;
}
} else {
hook.call(directive);
const isInitHook = arr[i] < 0;
if (isInitHook)
currentView[PREORDER_HOOK_FLAGS] += PreOrderHookFlags.NumberOfInitHooksCalledIncrementer;
if (lastNodeIndexFound < nodeIndexLimit || nodeIndexLimit == -1) {
callHook(currentView, initPhase, arr, i);
currentView[PREORDER_HOOK_FLAGS] =
(currentView[PREORDER_HOOK_FLAGS] & PreOrderHookFlags.NumberOfInitHooksCalledMask) + i +
2;
}
i++;
}
}
}
/**
* Execute one hook against the current `LView`.
*
* @param currentView The current view
* @param initPhaseState the current state of the init phase
* @param arr The array in which the hooks are found
* @param i The current index within the hook data array
*/
function callHook(currentView: LView, initPhase: InitPhaseState, arr: HookData, i: number) {
const isInitHook = arr[i] < 0;
const hook = arr[i + 1] as() => void;
const directiveIndex = isInitHook ? -arr[i] : arr[i] as number;
const directive = currentView[directiveIndex];
if (isInitHook) {
const indexWithintInitPhase = currentView[FLAGS] >> LViewFlags.IndexWithinInitPhaseShift;
// The init phase state must be always checked here as it may have been recursively
// updated
if (indexWithintInitPhase <
(currentView[PREORDER_HOOK_FLAGS] >> PreOrderHookFlags.NumberOfInitHooksCalledShift) &&
(currentView[FLAGS] & LViewFlags.InitPhaseStateMask) === initPhase) {
currentView[FLAGS] += LViewFlags.IndexWithinInitPhaseIncrementer;
hook.call(directive);
}
} else {
hook.call(directive);
}
}

View File

@ -57,6 +57,8 @@ export {
elementStyleProp,
elementStylingApply,
flushHooksUpTo,
listener,
store,
load,

View File

@ -24,7 +24,7 @@ import {attachPatchData, getComponentViewByInstance} from './context_discovery';
import {attachLContainerDebug, attachLViewDebug} from './debug';
import {diPublicInInjector, getNodeInjectable, getOrCreateInjectable, getOrCreateNodeInjectorForNode, injectAttributeImpl} from './di';
import {throwMultipleComponentError} from './errors';
import {executeHooks, executeInitHooks, registerPostOrderHooks, registerPreOrderHooks} from './hooks';
import {executeHooks, executePreOrderHooks, registerPostOrderHooks, registerPreOrderHooks} from './hooks';
import {ACTIVE_INDEX, LContainer, VIEWS} from './interfaces/container';
import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, PipeDefListOrFactory, RenderFlags, ViewQueriesFunction} from './interfaces/definition';
import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from './interfaces/injector';
@ -48,7 +48,7 @@ import {NO_CHANGE} from './tokens';
import {attrsStylingIndexOf, setUpAttributes} from './util/attrs_utils';
import {INTERPOLATION_DELIMITER, renderStringify} from './util/misc_utils';
import {findComponentView, getLViewParent, getRootContext, getRootView} from './util/view_traversal_utils';
import {getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, isComponent, isComponentDef, isContentQueryHost, isRootView, loadInternal, readPatchedLView, unwrapRNode, viewAttachedToChangeDetector} from './util/view_utils';
import {getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, isComponent, isComponentDef, isContentQueryHost, isRootView, loadInternal, readPatchedLView, resetPreOrderHookFlags, unwrapRNode, viewAttachedToChangeDetector} from './util/view_utils';
@ -84,16 +84,17 @@ export function refreshDescendantViews(lView: LView) {
if (!creationMode) {
const checkNoChangesMode = getCheckNoChangesMode();
executeInitHooks(lView, tView, checkNoChangesMode);
executePreOrderHooks(lView, tView, checkNoChangesMode, undefined);
refreshDynamicEmbeddedViews(lView);
// Content query results must be refreshed before content hooks are called.
refreshContentQueries(tView, lView);
resetPreOrderHookFlags(lView);
executeHooks(
lView, tView.contentHooks, tView.contentCheckHooks, checkNoChangesMode,
InitPhaseState.AfterContentInitHooksToBeRun);
InitPhaseState.AfterContentInitHooksToBeRun, undefined);
setHostBindings(tView, lView);
}
@ -180,6 +181,7 @@ export function createLView<T>(
const lView = tView.blueprint.slice() as LView;
lView[HOST] = host;
lView[FLAGS] = flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.FirstLViewPass;
resetPreOrderHookFlags(lView);
lView[PARENT] = lView[DECLARATION_VIEW] = parentLView;
lView[CONTEXT] = context;
lView[RENDERER_FACTORY] = (rendererFactory || parentLView && parentLView[RENDERER_FACTORY]) !;
@ -405,6 +407,7 @@ export function renderEmbeddedTemplate<T>(viewToRender: LView, tView: TView, con
setPreviousOrParentTNode(null !);
oldView = enterView(viewToRender, viewToRender[T_HOST]);
resetPreOrderHookFlags(viewToRender);
namespaceHTML();
tView.template !(getRenderFlags(viewToRender), context);
// This must be set to false immediately after the first creation run because in an
@ -459,6 +462,7 @@ function renderComponentOrTemplate<T>(
}
// update mode pass
resetPreOrderHookFlags(hostView);
templateFn && templateFn(RenderFlags.Update, context);
refreshDescendantViews(hostView);
} finally {
@ -807,8 +811,8 @@ export function createTView(
firstTemplatePass: true,
staticViewQueries: false,
staticContentQueries: false,
initHooks: null,
checkHooks: null,
preOrderHooks: null,
preOrderCheckHooks: null,
contentHooks: null,
contentCheckHooks: null,
viewHooks: null,
@ -1056,6 +1060,17 @@ export function elementEnd(): void {
}
}
/**
* Flushes all the lifecycle hooks for directives up until (and excluding) that node index
*
* @param index The index of the element in the `LView`
*/
export function flushHooksUpTo(index: number): void {
const lView = getLView();
executePreOrderHooks(lView, lView[TVIEW], getCheckNoChangesMode(), index);
}
/**
* Updates the value of removes an attribute on an Element.
*
@ -1748,6 +1763,10 @@ function resolveDirectives(
if (def.providersResolver) def.providersResolver(def);
}
generateExpandoInstructionBlock(tView, tNode, directives.length);
const initialPreOrderHooksLength = (tView.preOrderHooks && tView.preOrderHooks.length) || 0;
const initialPreOrderCheckHooksLength =
(tView.preOrderCheckHooks && tView.preOrderCheckHooks.length) || 0;
const nodeIndex = tNode.index - HEADER_OFFSET;
for (let i = 0; i < directives.length; i++) {
const def = directives[i] as DirectiveDef<any>;
@ -1758,7 +1777,9 @@ function resolveDirectives(
// Init hooks are queued now so ngOnInit is called in host components before
// any projected components.
registerPreOrderHooks(directiveDefIdx, def, tView);
registerPreOrderHooks(
directiveDefIdx, def, tView, nodeIndex, initialPreOrderHooksLength,
initialPreOrderCheckHooksLength);
}
}
if (exportsMap) cacheMatchingLocalNames(tNode, localRefs, exportsMap);
@ -2276,7 +2297,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(lView, tView, getCheckNoChangesMode());
executePreOrderHooks(lView, tView, getCheckNoChangesMode(), undefined);
}
/**
@ -2440,6 +2461,7 @@ export function embeddedViewEnd(): void {
refreshDescendantViews(lView); // creation mode pass
lView[FLAGS] &= ~LViewFlags.CreationMode;
}
resetPreOrderHookFlags(lView);
refreshDescendantViews(lView); // update mode pass
const lContainer = lView[PARENT] as LContainer;
ngDevMode && assertLContainerOrUndefined(lContainer);
@ -2834,6 +2856,7 @@ export function checkView<T>(hostView: LView, component: T) {
const creationMode = isCreationMode(hostView);
try {
resetPreOrderHookFlags(hostView);
namespaceHTML();
creationMode && executeViewQueryFn(RenderFlags.Create, hostTView, component);
templateFn(getRenderFlags(hostView), component);

View File

@ -45,8 +45,9 @@ export const CHILD_HEAD = 14;
export const CHILD_TAIL = 15;
export const CONTENT_QUERIES = 16;
export const DECLARATION_VIEW = 17;
export const PREORDER_HOOK_FLAGS = 18;
/** Size of LView's header. Necessary to adjust for it when setting slots. */
export const HEADER_OFFSET = 19;
export const HEADER_OFFSET = 20;
// This interface replaces the real LView interface if it is an arg or a
@ -215,6 +216,11 @@ export interface LView extends Array<any> {
* context.
*/
[DECLARATION_VIEW]: LView|null;
/**
* More flags for this view. See PreOrderHookFlags for more info.
*/
[PREORDER_HOOK_FLAGS]: PreOrderHookFlags;
}
/** Flags associated with an LView (saved in LView[FLAGS]) */
@ -296,6 +302,20 @@ export const enum InitPhaseState {
InitPhaseCompleted = 0b11,
}
/** More flags associated with an LView (saved in LView[FLAGS_MORE]) */
export const enum PreOrderHookFlags {
/** The index of the next pre-order hook to be called in the hooks array, on the first 16
bits */
IndexOfTheNextPreOrderHookMaskMask = 0b01111111111111111,
/**
* The number of init hooks that have already been called, on the last 16 bits
*/
NumberOfInitHooksCalledIncrementer = 0b010000000000000000,
NumberOfInitHooksCalledShift = 16,
NumberOfInitHooksCalledMask = 0b11111111111111110000000000000000,
}
/**
* Set of instructions used to process host bindings efficiently.
*
@ -438,21 +458,21 @@ export interface TView {
pipeRegistry: PipeDefList|null;
/**
* Array of ngOnInit and ngDoCheck hooks that should be executed for this view in
* Array of ngOnInit, ngOnChanges and ngDoCheck hooks that should be executed for this view in
* creation mode.
*
* Even indices: Directive index
* Odd indices: Hook function
*/
initHooks: HookData|null;
preOrderHooks: HookData|null;
/**
* Array of ngDoCheck hooks that should be executed for this view in update mode.
* Array of ngOnChanges and ngDoCheck hooks that should be executed for this view in update mode.
*
* Even indices: Directive index
* Odd indices: Hook function
*/
checkHooks: HookData|null;
preOrderCheckHooks: HookData|null;
/**
* Array of ngAfterContentInit and ngAfterContentChecked hooks that should be executed
@ -591,8 +611,14 @@ export interface RootContext {
/**
* Array of hooks that should be executed for a view and their directive indices.
*
* Even indices: Directive index
* Odd indices: Hook function
* For each node of the view, the following data is stored:
* 1) Node index (optional)
* 2) A series of number/function pairs where:
* - even indices are directive indices
* - odd indices are hook functions
*
* Special cases:
* - a negative directive index flags an init hook (ngOnInit, ngAfterContentInit, ngAfterViewInit)
*/
export type HookData = (number | (() => void))[];

View File

@ -99,6 +99,7 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵelementStylingMap': r3.elementStylingMap,
'ɵelementStyleProp': r3.elementStyleProp,
'ɵelementStylingApply': r3.elementStylingApply,
'ɵflushHooksUpTo': r3.flushHooksUpTo,
'ɵtemplate': r3.template,
'ɵtext': r3.text,
'ɵtextBinding': r3.textBinding,

View File

@ -13,6 +13,7 @@ import {executeHooks} from './hooks';
import {ComponentDef, DirectiveDef} from './interfaces/definition';
import {TElementNode, TNode, TViewNode} from './interfaces/node';
import {BINDING_INDEX, CONTEXT, DECLARATION_VIEW, FLAGS, InitPhaseState, LView, LViewFlags, OpaqueViewState, TVIEW} from './interfaces/view';
import {resetPreOrderHookFlags} from './util/view_utils';
@ -304,9 +305,10 @@ export function leaveView(newView: LView): void {
lView[FLAGS] &= ~LViewFlags.CreationMode;
} else {
try {
resetPreOrderHookFlags(lView);
executeHooks(
lView, tView.viewHooks, tView.viewCheckHooks, checkNoChangesMode,
InitPhaseState.AfterViewInitHooksToBeRun);
InitPhaseState.AfterViewInitHooksToBeRun, undefined);
} finally {
// Views are clean and in update mode after being checked, so these bits are cleared
lView[FLAGS] &= ~(LViewFlags.Dirty | LViewFlags.FirstLViewPass);

View File

@ -13,7 +13,7 @@ import {ComponentDef, DirectiveDef} from '../interfaces/definition';
import {TNode, TNodeFlags} from '../interfaces/node';
import {RNode} from '../interfaces/renderer';
import {StylingContext} from '../interfaces/styling';
import {FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, PARENT, TData, TVIEW} from '../interfaces/view';
import {FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, PARENT, PREORDER_HOOK_FLAGS, TData, TVIEW} from '../interfaces/view';
@ -197,3 +197,11 @@ export function viewAttachedToChangeDetector(view: LView): boolean {
export function viewAttachedToContainer(view: LView): boolean {
return isLContainer(view[PARENT]);
}
/**
* Resets the pre-order hook flags of the view.
* @param lView the LView on which the flags are reset
*/
export function resetPreOrderHookFlags(lView: LView) {
lView[PREORDER_HOOK_FLAGS] = 0;
}