| 
									
										
										
										
											2018-10-25 16:38:52 -07:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  |  * @license | 
					
						
							|  |  |  |  * Copyright Google Inc. All Rights Reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Use of this source code is governed by an MIT-style license that can be | 
					
						
							|  |  |  |  * found in the LICENSE file at https://angular.io/license
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import {resolveForwardRef} from '../di/forward_ref'; | 
					
						
							| 
									
										
										
										
											2019-01-31 10:38:43 +01:00
										 |  |  | import {ClassProvider, Provider} from '../di/interface/provider'; | 
					
						
							|  |  |  | import {isClassProvider, isTypeProvider, providerToFactory} from '../di/r3_injector'; | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | import {diPublicInInjector, getNodeInjectable, getOrCreateNodeInjectorForNode} from './di'; | 
					
						
							| 
									
										
										
										
											2019-05-17 18:49:21 -07:00
										 |  |  | import {ɵɵdirectiveInject} from './instructions/all'; | 
					
						
							| 
									
										
										
										
											2019-04-01 15:36:43 -07:00
										 |  |  | import {DirectiveDef} from './interfaces/definition'; | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  | import {NodeInjectorFactory} from './interfaces/injector'; | 
					
						
							| 
									
										
										
										
											2019-04-01 15:36:43 -07:00
										 |  |  | import {TContainerNode, TElementContainerNode, TElementNode, TNodeProviderIndexes} from './interfaces/node'; | 
					
						
							| 
									
										
										
										
											2019-07-19 13:21:21 -07:00
										 |  |  | import {isComponentDef} from './interfaces/type_checks'; | 
					
						
							| 
									
										
										
										
											2018-11-21 21:14:06 -08:00
										 |  |  | import {LView, TData, TVIEW, TView} from './interfaces/view'; | 
					
						
							|  |  |  | import {getLView, getPreviousOrParentTNode} from './state'; | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Resolves the providers which are defined in the DirectiveDef. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * When inserting the tokens and the factories in their respective arrays, we can assume that | 
					
						
							|  |  |  |  * this method is called first for the component (if any), and then for other directives on the same | 
					
						
							|  |  |  |  * node. | 
					
						
							|  |  |  |  * As a consequence,the providers are always processed in that order: | 
					
						
							|  |  |  |  * 1) The view providers of the component | 
					
						
							|  |  |  |  * 2) The providers of the component | 
					
						
							|  |  |  |  * 3) The providers of the other directives | 
					
						
							|  |  |  |  * This matches the structure of the injectables arrays of a view (for each node). | 
					
						
							|  |  |  |  * So the tokens and the factories can be pushed at the end of the arrays, except | 
					
						
							|  |  |  |  * in one case for multi providers. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param def the directive definition | 
					
						
							|  |  |  |  * @param providers: Array of `providers`. | 
					
						
							|  |  |  |  * @param viewProviders: Array of `viewProviders`. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export function providersResolver<T>( | 
					
						
							|  |  |  |     def: DirectiveDef<T>, providers: Provider[], viewProviders: Provider[]): void { | 
					
						
							| 
									
										
										
										
											2018-11-21 21:14:06 -08:00
										 |  |  |   const lView = getLView(); | 
					
						
							|  |  |  |   const tView: TView = lView[TVIEW]; | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  |   if (tView.firstTemplatePass) { | 
					
						
							|  |  |  |     const isComponent = isComponentDef(def); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // The list of view providers is processed first, and the flags are updated
 | 
					
						
							|  |  |  |     resolveProvider(viewProviders, tView.data, tView.blueprint, isComponent, true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Then, the list of providers is processed, and the flags are updated
 | 
					
						
							|  |  |  |     resolveProvider(providers, tView.data, tView.blueprint, isComponent, false); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2018-10-25 16:38:52 -07:00
										 |  |  |  * Resolves a provider and publishes it to the DI system. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  | function resolveProvider( | 
					
						
							|  |  |  |     provider: Provider, tInjectables: TData, lInjectablesBlueprint: NodeInjectorFactory[], | 
					
						
							|  |  |  |     isComponent: boolean, isViewProvider: boolean): void { | 
					
						
							|  |  |  |   provider = resolveForwardRef(provider); | 
					
						
							|  |  |  |   if (Array.isArray(provider)) { | 
					
						
							|  |  |  |     // Recursively call `resolveProvider`
 | 
					
						
							|  |  |  |     // Recursion is OK in this case because this code will not be in hot-path once we implement
 | 
					
						
							|  |  |  |     // cloning of the initial state.
 | 
					
						
							|  |  |  |     for (let i = 0; i < provider.length; i++) { | 
					
						
							|  |  |  |       resolveProvider( | 
					
						
							|  |  |  |           provider[i], tInjectables, lInjectablesBlueprint, isComponent, isViewProvider); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } else { | 
					
						
							| 
									
										
										
										
											2018-11-21 21:14:06 -08:00
										 |  |  |     const lView = getLView(); | 
					
						
							| 
									
										
										
										
											2019-07-10 17:53:42 +02:00
										 |  |  |     const tView = lView[TVIEW]; | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  |     let token: any = isTypeProvider(provider) ? provider : resolveForwardRef(provider.provide); | 
					
						
							|  |  |  |     let providerFactory: () => any = providerToFactory(provider); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-28 15:54:38 -08:00
										 |  |  |     const tNode = getPreviousOrParentTNode(); | 
					
						
							|  |  |  |     const beginIndex = tNode.providerIndexes & TNodeProviderIndexes.ProvidersStartIndexMask; | 
					
						
							|  |  |  |     const endIndex = tNode.directiveStart; | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  |     const cptViewProvidersCount = | 
					
						
							| 
									
										
										
										
											2018-11-28 15:54:38 -08:00
										 |  |  |         tNode.providerIndexes >> TNodeProviderIndexes.CptViewProvidersCountShift; | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-31 10:38:43 +01:00
										 |  |  |     if (isClassProvider(provider) || isTypeProvider(provider)) { | 
					
						
							|  |  |  |       const prototype = ((provider as ClassProvider).useClass || provider).prototype; | 
					
						
							|  |  |  |       const ngOnDestroy = prototype.ngOnDestroy; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (ngOnDestroy) { | 
					
						
							|  |  |  |         (tView.destroyHooks || (tView.destroyHooks = [])).push(tInjectables.length, ngOnDestroy); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  |     if (isTypeProvider(provider) || !provider.multi) { | 
					
						
							|  |  |  |       // Single provider case: the factory is created and pushed immediately
 | 
					
						
							| 
									
										
										
										
											2019-05-17 18:49:21 -07:00
										 |  |  |       const factory = new NodeInjectorFactory(providerFactory, isViewProvider, ɵɵdirectiveInject); | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  |       const existingFactoryIndex = indexOf( | 
					
						
							|  |  |  |           token, tInjectables, isViewProvider ? beginIndex : beginIndex + cptViewProvidersCount, | 
					
						
							|  |  |  |           endIndex); | 
					
						
							|  |  |  |       if (existingFactoryIndex == -1) { | 
					
						
							|  |  |  |         diPublicInInjector( | 
					
						
							|  |  |  |             getOrCreateNodeInjectorForNode( | 
					
						
							| 
									
										
										
										
											2018-11-28 15:54:38 -08:00
										 |  |  |                 tNode as TElementNode | TContainerNode | TElementContainerNode, lView), | 
					
						
							| 
									
										
										
										
											2019-07-10 17:53:42 +02:00
										 |  |  |             tView, token); | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  |         tInjectables.push(token); | 
					
						
							| 
									
										
										
										
											2018-11-28 15:54:38 -08:00
										 |  |  |         tNode.directiveStart++; | 
					
						
							|  |  |  |         tNode.directiveEnd++; | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  |         if (isViewProvider) { | 
					
						
							| 
									
										
										
										
											2018-11-28 15:54:38 -08:00
										 |  |  |           tNode.providerIndexes += TNodeProviderIndexes.CptViewProvidersCountShifter; | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |         lInjectablesBlueprint.push(factory); | 
					
						
							| 
									
										
										
										
											2018-11-21 21:14:06 -08:00
										 |  |  |         lView.push(factory); | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  |       } else { | 
					
						
							|  |  |  |         lInjectablesBlueprint[existingFactoryIndex] = factory; | 
					
						
							| 
									
										
										
										
											2018-11-21 21:14:06 -08:00
										 |  |  |         lView[existingFactoryIndex] = factory; | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  |       } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       // Multi provider case:
 | 
					
						
							|  |  |  |       // We create a multi factory which is going to aggregate all the values.
 | 
					
						
							|  |  |  |       // Since the output of such a factory depends on content or view injection,
 | 
					
						
							|  |  |  |       // we create two of them, which are linked together.
 | 
					
						
							|  |  |  |       //
 | 
					
						
							|  |  |  |       // The first one (for view providers) is always in the first block of the injectables array,
 | 
					
						
							|  |  |  |       // and the second one (for providers) is always in the second block.
 | 
					
						
							|  |  |  |       // This is important because view providers have higher priority. When a multi token
 | 
					
						
							|  |  |  |       // is being looked up, the view providers should be found first.
 | 
					
						
							|  |  |  |       // Note that it is not possible to have a multi factory in the third block (directive block).
 | 
					
						
							|  |  |  |       //
 | 
					
						
							|  |  |  |       // The algorithm to process multi providers is as follows:
 | 
					
						
							|  |  |  |       // 1) If the multi provider comes from the `viewProviders` of the component:
 | 
					
						
							|  |  |  |       //   a) If the special view providers factory doesn't exist, it is created and pushed.
 | 
					
						
							|  |  |  |       //   b) Else, the multi provider is added to the existing multi factory.
 | 
					
						
							|  |  |  |       // 2) If the multi provider comes from the `providers` of the component or of another
 | 
					
						
							|  |  |  |       // directive:
 | 
					
						
							|  |  |  |       //   a) If the multi factory doesn't exist, it is created and provider pushed into it.
 | 
					
						
							|  |  |  |       //      It is also linked to the multi factory for view providers, if it exists.
 | 
					
						
							|  |  |  |       //   b) Else, the multi provider is added to the existing multi factory.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const existingProvidersFactoryIndex = | 
					
						
							|  |  |  |           indexOf(token, tInjectables, beginIndex + cptViewProvidersCount, endIndex); | 
					
						
							|  |  |  |       const existingViewProvidersFactoryIndex = | 
					
						
							|  |  |  |           indexOf(token, tInjectables, beginIndex, beginIndex + cptViewProvidersCount); | 
					
						
							|  |  |  |       const doesProvidersFactoryExist = existingProvidersFactoryIndex >= 0 && | 
					
						
							|  |  |  |           lInjectablesBlueprint[existingProvidersFactoryIndex]; | 
					
						
							|  |  |  |       const doesViewProvidersFactoryExist = existingViewProvidersFactoryIndex >= 0 && | 
					
						
							|  |  |  |           lInjectablesBlueprint[existingViewProvidersFactoryIndex]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (isViewProvider && !doesViewProvidersFactoryExist || | 
					
						
							|  |  |  |           !isViewProvider && !doesProvidersFactoryExist) { | 
					
						
							|  |  |  |         // Cases 1.a and 2.a
 | 
					
						
							|  |  |  |         diPublicInInjector( | 
					
						
							|  |  |  |             getOrCreateNodeInjectorForNode( | 
					
						
							| 
									
										
										
										
											2018-11-28 15:54:38 -08:00
										 |  |  |                 tNode as TElementNode | TContainerNode | TElementContainerNode, lView), | 
					
						
							| 
									
										
										
										
											2019-07-10 17:53:42 +02:00
										 |  |  |             tView, token); | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  |         const factory = multiFactory( | 
					
						
							|  |  |  |             isViewProvider ? multiViewProvidersFactoryResolver : multiProvidersFactoryResolver, | 
					
						
							|  |  |  |             lInjectablesBlueprint.length, isViewProvider, isComponent, providerFactory); | 
					
						
							|  |  |  |         if (!isViewProvider && doesViewProvidersFactoryExist) { | 
					
						
							|  |  |  |           lInjectablesBlueprint[existingViewProvidersFactoryIndex].providerFactory = factory; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         tInjectables.push(token); | 
					
						
							| 
									
										
										
										
											2018-11-28 15:54:38 -08:00
										 |  |  |         tNode.directiveStart++; | 
					
						
							|  |  |  |         tNode.directiveEnd++; | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  |         if (isViewProvider) { | 
					
						
							| 
									
										
										
										
											2018-11-28 15:54:38 -08:00
										 |  |  |           tNode.providerIndexes += TNodeProviderIndexes.CptViewProvidersCountShifter; | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |         lInjectablesBlueprint.push(factory); | 
					
						
							| 
									
										
										
										
											2018-11-21 21:14:06 -08:00
										 |  |  |         lView.push(factory); | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  |       } else { | 
					
						
							|  |  |  |         // Cases 1.b and 2.b
 | 
					
						
							|  |  |  |         multiFactoryAdd( | 
					
						
							|  |  |  |             lInjectablesBlueprint ![isViewProvider ? existingViewProvidersFactoryIndex : existingProvidersFactoryIndex], | 
					
						
							|  |  |  |             providerFactory, !isViewProvider && isComponent); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (!isViewProvider && isComponent && doesViewProvidersFactoryExist) { | 
					
						
							|  |  |  |         lInjectablesBlueprint[existingViewProvidersFactoryIndex].componentProviders !++; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2018-10-25 16:38:52 -07:00
										 |  |  |  * Add a factory in a multi factory. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  | function multiFactoryAdd( | 
					
						
							|  |  |  |     multiFactory: NodeInjectorFactory, factory: () => any, isComponentProvider: boolean): void { | 
					
						
							|  |  |  |   multiFactory.multi !.push(factory); | 
					
						
							|  |  |  |   if (isComponentProvider) { | 
					
						
							|  |  |  |     multiFactory.componentProviders !++; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2018-10-25 16:38:52 -07:00
										 |  |  |  * Returns the index of item in the array, but only in the begin to end range. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  | function indexOf(item: any, arr: any[], begin: number, end: number) { | 
					
						
							|  |  |  |   for (let i = begin; i < end; i++) { | 
					
						
							|  |  |  |     if (arr[i] === item) return i; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2018-10-25 16:38:52 -07:00
										 |  |  |  * Use this with `multi` `providers`. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  | function multiProvidersFactoryResolver( | 
					
						
							| 
									
										
										
										
											2019-06-14 09:27:41 +02:00
										 |  |  |     this: NodeInjectorFactory, _: undefined, tData: TData, lData: LView, | 
					
						
							|  |  |  |     tNode: TElementNode): any[] { | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  |   return multiResolve(this.multi !, []); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2018-10-25 16:38:52 -07:00
										 |  |  |  * Use this with `multi` `viewProviders`. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This factory knows how to concatenate itself with the existing `multi` `providers`. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  | function multiViewProvidersFactoryResolver( | 
					
						
							| 
									
										
										
										
											2019-06-14 09:27:41 +02:00
										 |  |  |     this: NodeInjectorFactory, _: undefined, tData: TData, lData: LView, | 
					
						
							|  |  |  |     tNode: TElementNode): any[] { | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  |   const factories = this.multi !; | 
					
						
							|  |  |  |   let result: any[]; | 
					
						
							|  |  |  |   if (this.providerFactory) { | 
					
						
							|  |  |  |     const componentCount = this.providerFactory.componentProviders !; | 
					
						
							|  |  |  |     const multiProviders = getNodeInjectable(tData, lData, this.providerFactory !.index !, tNode); | 
					
						
							|  |  |  |     // Copy the section of the array which contains `multi` `providers` from the component
 | 
					
						
							|  |  |  |     result = multiProviders.slice(0, componentCount); | 
					
						
							|  |  |  |     // Insert the `viewProvider` instances.
 | 
					
						
							|  |  |  |     multiResolve(factories, result); | 
					
						
							|  |  |  |     // Copy the section of the array which contains `multi` `providers` from other directives
 | 
					
						
							|  |  |  |     for (let i = componentCount; i < multiProviders.length; i++) { | 
					
						
							|  |  |  |       result.push(multiProviders[i]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     result = []; | 
					
						
							|  |  |  |     // Insert the `viewProvider` instances.
 | 
					
						
							|  |  |  |     multiResolve(factories, result); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2018-10-25 16:38:52 -07:00
										 |  |  |  * Maps an array of factories into an array of values. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  | function multiResolve(factories: Array<() => any>, result: any[]): any[] { | 
					
						
							|  |  |  |   for (let i = 0; i < factories.length; i++) { | 
					
						
							|  |  |  |     const factory = factories[i] !as() => null; | 
					
						
							|  |  |  |     result.push(factory()); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2018-10-25 16:38:52 -07:00
										 |  |  |  * Creates a multi factory. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  | function multiFactory( | 
					
						
							| 
									
										
										
										
											2018-11-21 21:14:06 -08:00
										 |  |  |     factoryFn: ( | 
					
						
							| 
									
										
										
										
											2019-06-14 09:27:41 +02:00
										 |  |  |         this: NodeInjectorFactory, _: undefined, tData: TData, lData: LView, tNode: TElementNode) => | 
					
						
							|  |  |  |         any, | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  |     index: number, isViewProvider: boolean, isComponent: boolean, | 
					
						
							|  |  |  |     f: () => any): NodeInjectorFactory { | 
					
						
							| 
									
										
										
										
											2019-05-17 18:49:21 -07:00
										 |  |  |   const factory = new NodeInjectorFactory(factoryFn, isViewProvider, ɵɵdirectiveInject); | 
					
						
							| 
									
										
										
										
											2018-10-18 09:23:18 +02:00
										 |  |  |   factory.multi = []; | 
					
						
							|  |  |  |   factory.index = index; | 
					
						
							|  |  |  |   factory.componentProviders = 0; | 
					
						
							|  |  |  |   multiFactoryAdd(factory, f, isComponent && !isViewProvider); | 
					
						
							|  |  |  |   return factory; | 
					
						
							|  |  |  | } |