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
 | ||||
|  | ||||
							
								
								
									
										153
									
								
								packages/core/src/render3/hooks.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								packages/core/src/render3/hooks.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user