| 
									
										
										
										
											2017-12-01 14:23:03 -08: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
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-14 18:05:41 -08:00
										 |  |  | // We are temporarily importing the existing viewEngine from core so we can be sure we are
 | 
					
						
							|  |  |  | // correctly implementing its interfaces for backwards compatibility.
 | 
					
						
							| 
									
										
										
										
											2018-03-14 21:32:09 -07:00
										 |  |  | import {Type} from '../core'; | 
					
						
							| 
									
										
										
										
											2017-12-20 10:47:22 -08:00
										 |  |  | import {Injector} from '../di/injector'; | 
					
						
							|  |  |  | import {ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory'; | 
					
						
							| 
									
										
										
										
											2017-12-14 15:03:46 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-14 21:32:09 -07:00
										 |  |  | import {assertComponentType, assertNotNull} from './assert'; | 
					
						
							| 
									
										
										
										
											2018-03-16 20:31:24 -07:00
										 |  |  | import {queueInitHooks, queueLifecycleHooks} from './hooks'; | 
					
						
							| 
									
										
										
										
											2018-03-25 21:32:39 -07:00
										 |  |  | import {CLEAN_PROMISE, ROOT_DIRECTIVE_INDICES, _getComponentHostLElementNode, baseDirectiveCreate, createLView, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, getRootView, hostElement, initChangeDetectorIfExisting, leaveView, locateHostElement, setHostBindings} from './instructions'; | 
					
						
							| 
									
										
										
										
											2018-04-13 23:02:29 -07:00
										 |  |  | import {ComponentDef, ComponentType} from './interfaces/definition'; | 
					
						
							| 
									
										
										
										
											2018-03-20 19:06:49 -07:00
										 |  |  | import {LElementNode, TNodeFlags} from './interfaces/node'; | 
					
						
							| 
									
										
										
										
											2018-02-26 16:58:15 -08:00
										 |  |  | import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; | 
					
						
							| 
									
										
										
										
											2018-03-08 16:55:47 -08:00
										 |  |  | import {LView, LViewFlags, RootContext} from './interfaces/view'; | 
					
						
							| 
									
										
										
										
											2018-02-26 16:58:15 -08:00
										 |  |  | import {stringify} from './util'; | 
					
						
							|  |  |  | import {createViewRef} from './view_ref'; | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-09 16:43:12 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-16 20:31:24 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-14 18:05:41 -08:00
										 |  |  | /** Options that control how the component should be bootstrapped. */ | 
					
						
							|  |  |  | export interface CreateComponentOptions { | 
					
						
							|  |  |  |   /** Which renderer factory to use. */ | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |   rendererFactory?: RendererFactory3; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							| 
									
										
										
										
											2017-12-14 18:05:41 -08:00
										 |  |  |    * Host element on which the component will be bootstrapped. If not specified, | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |    * the component definition's `tag` is used to query the existing DOM for the | 
					
						
							|  |  |  |    * element to bootstrap. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   host?: RElement|string; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-14 18:05:41 -08:00
										 |  |  |   /** Module injector for the component. If unspecified, the injector will be NULL_INJECTOR. */ | 
					
						
							| 
									
										
										
										
											2017-12-20 10:47:22 -08:00
										 |  |  |   injector?: Injector; | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							| 
									
										
										
										
											2017-12-14 18:05:41 -08:00
										 |  |  |    * List of features to be applied to the created component. Features are simply | 
					
						
							|  |  |  |    * functions that decorate a component with a certain behavior. | 
					
						
							|  |  |  |    * | 
					
						
							| 
									
										
										
										
											2018-03-06 10:13:49 -08:00
										 |  |  |    * Typically, the features in this list are features that cannot be added to the | 
					
						
							|  |  |  |    * other features list in the component definition because they rely on other factors. | 
					
						
							|  |  |  |    * | 
					
						
							| 
									
										
										
										
											2018-03-06 11:58:08 -08:00
										 |  |  |    * Example: `RootLifecycleHooks` is a function that adds lifecycle hook capabilities | 
					
						
							| 
									
										
										
										
											2018-03-06 10:13:49 -08:00
										 |  |  |    * to root components in a tree-shakable way. It cannot be added to the component | 
					
						
							|  |  |  |    * features list because there's no way of knowing when the component will be used as | 
					
						
							|  |  |  |    * a root component. | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |    */ | 
					
						
							| 
									
										
										
										
											2018-04-13 23:02:29 -07:00
										 |  |  |   hostFeatures?: (<T>(component: T, componentDef: ComponentDef<T>) => void)[]; | 
					
						
							| 
									
										
										
										
											2018-02-03 20:34:30 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * A function which is used to schedule change detection work in the future. | 
					
						
							|  |  |  |    * | 
					
						
							|  |  |  |    * When marking components as dirty, it is necessary to schedule the work of | 
					
						
							|  |  |  |    * change detection in the future. This is done to coalesce multiple | 
					
						
							|  |  |  |    * {@link markDirty} calls into a single changed detection processing. | 
					
						
							|  |  |  |    * | 
					
						
							|  |  |  |    * The default value of the scheduler is the `requestAnimationFrame` function. | 
					
						
							|  |  |  |    * | 
					
						
							|  |  |  |    * It is also useful to override this function for testing purposes. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   scheduler?: (work: () => void) => void; | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2017-12-14 18:05:41 -08:00
										 |  |  |  * Bootstraps a component, then creates and returns a `ComponentRef` for that component. | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * @param componentType Component to bootstrap | 
					
						
							|  |  |  |  * @param options Optional parameters which control bootstrapping | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export function createComponentRef<T>( | 
					
						
							| 
									
										
										
										
											2017-12-20 10:47:22 -08:00
										 |  |  |     componentType: ComponentType<T>, opts: CreateComponentOptions): viewEngine_ComponentRef<T> { | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |   const component = renderComponent(componentType, opts); | 
					
						
							| 
									
										
										
										
											2018-03-08 16:55:47 -08:00
										 |  |  |   const hostView = _getComponentHostLElementNode(component).data as LView; | 
					
						
							|  |  |  |   const hostViewRef = createViewRef(hostView, component); | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |   return { | 
					
						
							|  |  |  |     location: {nativeElement: getHostElement(component)}, | 
					
						
							|  |  |  |     injector: opts.injector || NULL_INJECTOR, | 
					
						
							|  |  |  |     instance: component, | 
					
						
							| 
									
										
										
										
											2018-03-08 16:55:47 -08:00
										 |  |  |     hostView: hostViewRef, | 
					
						
							|  |  |  |     changeDetectorRef: hostViewRef, | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |     componentType: componentType, | 
					
						
							| 
									
										
										
										
											2017-12-14 18:05:41 -08:00
										 |  |  |     // TODO: implement destroy and onDestroy
 | 
					
						
							|  |  |  |     destroy: () => {}, | 
					
						
							|  |  |  |     onDestroy: (cb: Function) => {} | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TODO: A hack to not pull in the NullInjector from @angular/core.
 | 
					
						
							| 
									
										
										
										
											2017-12-20 10:47:22 -08:00
										 |  |  | export const NULL_INJECTOR: Injector = { | 
					
						
							| 
									
										
										
										
											2017-12-14 18:05:41 -08:00
										 |  |  |   get: (token: any, notFoundValue?: any) => { | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |     throw new Error('NullInjector: Not found: ' + stringify(token)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2017-12-14 18:05:41 -08:00
										 |  |  |  * Bootstraps a Component into an existing host element and returns an instance | 
					
						
							|  |  |  |  * of the component. | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2018-02-03 20:34:30 -08:00
										 |  |  |  * Use this function to bootstrap a component into the DOM tree. Each invocation | 
					
						
							|  |  |  |  * of this function will create a separate tree of components, injectors and | 
					
						
							|  |  |  |  * change detection cycles and lifetimes. To dynamically insert a new component | 
					
						
							|  |  |  |  * into an existing tree such that it shares the same injection, change detection | 
					
						
							|  |  |  |  * and object lifetime, use {@link ViewContainer#createComponent}. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |  * @param componentType Component to bootstrap | 
					
						
							|  |  |  |  * @param options Optional parameters which control bootstrapping | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export function renderComponent<T>( | 
					
						
							| 
									
										
										
										
											2018-03-14 21:32:09 -07:00
										 |  |  |     componentType: ComponentType<T>| | 
					
						
							|  |  |  |         Type<T>/* Type as workaround for: Microsoft/TypeScript/issues/4881 */ | 
					
						
							|  |  |  |     , | 
					
						
							|  |  |  |     opts: CreateComponentOptions = {}): T { | 
					
						
							|  |  |  |   ngDevMode && assertComponentType(componentType); | 
					
						
							| 
									
										
										
										
											2017-12-11 16:30:46 +01:00
										 |  |  |   const rendererFactory = opts.rendererFactory || domRendererFactory3; | 
					
						
							| 
									
										
										
										
											2018-04-13 23:02:29 -07:00
										 |  |  |   const componentDef = (componentType as ComponentType<T>).ngComponentDef as ComponentDef<T>; | 
					
						
							| 
									
										
										
										
											2018-01-09 16:43:12 -08:00
										 |  |  |   if (componentDef.type != componentType) componentDef.type = componentType; | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |   let component: T; | 
					
						
							| 
									
										
										
										
											2018-03-29 16:41:45 -07:00
										 |  |  |   // The first index of the first selector is the tag name.
 | 
					
						
							|  |  |  |   const componentTag = componentDef.selectors ![0] ![0] as string; | 
					
						
							| 
									
										
										
										
											2018-03-25 21:32:39 -07:00
										 |  |  |   const hostNode = locateHostElement(rendererFactory, opts.host || componentTag); | 
					
						
							| 
									
										
										
										
											2018-02-03 20:34:30 -08:00
										 |  |  |   const rootContext: RootContext = { | 
					
						
							|  |  |  |     // Incomplete initialization due to circular reference.
 | 
					
						
							|  |  |  |     component: null !, | 
					
						
							| 
									
										
										
										
											2018-03-21 17:11:08 -07:00
										 |  |  |     scheduler: opts.scheduler || requestAnimationFrame.bind(window), | 
					
						
							| 
									
										
										
										
											2018-02-03 20:34:30 -08:00
										 |  |  |     clean: CLEAN_PROMISE, | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2018-04-12 15:54:16 -07:00
										 |  |  |   const rootView: LView = createLView( | 
					
						
							| 
									
										
										
										
											2018-03-27 15:53:48 -07:00
										 |  |  |       -1, rendererFactory.createRenderer(hostNode, componentDef.rendererType), | 
					
						
							|  |  |  |       createTView(null, null), null, rootContext, | 
					
						
							|  |  |  |       componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways); | 
					
						
							| 
									
										
										
										
											2018-04-12 15:54:16 -07:00
										 |  |  |   rootView.injector = opts.injector || null; | 
					
						
							| 
									
										
										
										
											2018-03-16 18:50:48 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   const oldView = enterView(rootView, null !); | 
					
						
							|  |  |  |   let elementNode: LElementNode; | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |   try { | 
					
						
							| 
									
										
										
										
											2018-03-25 21:32:39 -07:00
										 |  |  |     if (rendererFactory.begin) rendererFactory.begin(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-11 14:08:52 -08:00
										 |  |  |     // Create element node at index 0 in data array
 | 
					
						
							| 
									
										
										
										
											2018-03-25 21:32:39 -07:00
										 |  |  |     elementNode = hostElement(componentTag, hostNode, componentDef); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-21 15:10:34 -07:00
										 |  |  |     // Create directive instance with factory() and store at index 0 in directives array
 | 
					
						
							| 
									
										
										
										
											2018-03-18 13:18:54 -07:00
										 |  |  |     component = rootContext.component = | 
					
						
							| 
									
										
										
										
											2018-03-21 15:10:34 -07:00
										 |  |  |         baseDirectiveCreate(0, componentDef.factory(), componentDef) as T; | 
					
						
							| 
									
										
										
										
											2018-03-25 21:32:39 -07:00
										 |  |  |     initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data !); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     opts.hostFeatures && opts.hostFeatures.forEach((feature) => feature(component, componentDef)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     executeInitAndContentHooks(); | 
					
						
							|  |  |  |     setHostBindings(ROOT_DIRECTIVE_INDICES); | 
					
						
							|  |  |  |     detectChangesInternal(elementNode.data as LView, elementNode, componentDef, component); | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |   } finally { | 
					
						
							| 
									
										
										
										
											2018-03-25 21:32:39 -07:00
										 |  |  |     leaveView(oldView); | 
					
						
							|  |  |  |     if (rendererFactory.end) rendererFactory.end(); | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return component; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-03 20:34:30 -08:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2018-03-06 10:13:49 -08:00
										 |  |  |  * Used to enable lifecycle hooks on the root component. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Include this feature when calling `renderComponent` if the root component | 
					
						
							|  |  |  |  * you are rendering has lifecycle hooks defined. Otherwise, the hooks won't | 
					
						
							|  |  |  |  * be called properly. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Example: | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2018-03-06 11:58:08 -08:00
										 |  |  |  * ```
 | 
					
						
							| 
									
										
										
										
											2018-03-06 10:13:49 -08:00
										 |  |  |  * renderComponent(AppComponent, {features: [RootLifecycleHooks]}); | 
					
						
							| 
									
										
										
										
											2018-03-06 11:58:08 -08:00
										 |  |  |  * ```
 | 
					
						
							| 
									
										
										
										
											2018-03-06 10:13:49 -08:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-04-13 23:02:29 -07:00
										 |  |  | export function LifecycleHooksFeature(component: any, def: ComponentDef<any>): void { | 
					
						
							| 
									
										
										
										
											2018-03-06 10:13:49 -08:00
										 |  |  |   const elementNode = _getComponentHostLElementNode(component); | 
					
						
							| 
									
										
										
										
											2018-03-16 20:31:24 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-21 15:10:34 -07:00
										 |  |  |   // Root component is always created at dir index 0
 | 
					
						
							|  |  |  |   queueInitHooks(0, def.onInit, def.doCheck, elementNode.view.tView); | 
					
						
							| 
									
										
										
										
											2018-03-20 19:06:49 -07:00
										 |  |  |   queueLifecycleHooks(elementNode.tNode !.flags, elementNode.view); | 
					
						
							| 
									
										
										
										
											2018-03-06 10:13:49 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Retrieve the root context for any component by walking the parent `LView` until | 
					
						
							| 
									
										
										
										
											2018-02-03 20:34:30 -08:00
										 |  |  |  * reaching the root `LView`. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param component any component | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function getRootContext(component: any): RootContext { | 
					
						
							| 
									
										
										
										
											2018-03-06 11:58:08 -08:00
										 |  |  |   const rootContext = getRootView(component).context as RootContext; | 
					
						
							| 
									
										
										
										
											2018-02-03 20:34:30 -08:00
										 |  |  |   ngDevMode && assertNotNull(rootContext, 'rootContext'); | 
					
						
							|  |  |  |   return rootContext; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Retrieve the host element of the component. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Use this function to retrieve the host element of the component. The host | 
					
						
							|  |  |  |  * element is the element which the component is associated with. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param component Component for which the host element should be retrieved. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export function getHostElement<T>(component: T): HTMLElement { | 
					
						
							|  |  |  |   return _getComponentHostLElementNode(component).native as any; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Retrieves the rendered text for a given component. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function retrieves the host element of a component and | 
					
						
							|  |  |  |  * and then returns the `textContent` for that element. This implies | 
					
						
							|  |  |  |  * that the text returned will include re-projected content of | 
					
						
							|  |  |  |  * the component as well. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param component The component to return the content text for. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export function getRenderedText(component: any): string { | 
					
						
							|  |  |  |   const hostElement = getHostElement(component); | 
					
						
							|  |  |  |   return hostElement.textContent || ''; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Wait on component until it is rendered. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function returns a `Promise` which is resolved when the component's | 
					
						
							|  |  |  |  * change detection is executed. This is determined by finding the scheduler | 
					
						
							|  |  |  |  * associated with the `component`'s render tree and waiting until the scheduler | 
					
						
							|  |  |  |  * flushes. If nothing is scheduled, the function returns a resolved promise. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Example: | 
					
						
							|  |  |  |  * ```
 | 
					
						
							|  |  |  |  * await whenRendered(myComponent); | 
					
						
							|  |  |  |  * ```
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param component Component to wait upon | 
					
						
							|  |  |  |  * @returns Promise which resolves when the component is rendered. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export function whenRendered(component: any): Promise<null> { | 
					
						
							|  |  |  |   return getRootContext(component).clean; | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  | } |