feat(ivy): support ViewContainerRef.createComponent() (#24997)

PR Close #24997
This commit is contained in:
Marc Laval
2018-07-20 14:32:23 +02:00
committed by Victor Berchet
parent d523630ea2
commit 445b9a5627
9 changed files with 484 additions and 133 deletions

View File

@ -17,12 +17,12 @@ import {RendererFactory2} from '../render/api';
import {Type} from '../type';
import {assertComponentType, assertDefined} from './assert';
import {createRootContext} from './component';
import {baseDirectiveCreate, createLViewData, createTView, enterView, hostElement, initChangeDetectorIfExisting, locateHostElement} from './instructions';
import {ComponentDefInternal, ComponentType} from './interfaces/definition';
import {LElementNode} from './interfaces/node';
import {RElement} from './interfaces/renderer';
import {INJECTOR, LViewData, LViewFlags, RootContext} from './interfaces/view';
import {LifecycleHooksFeature, createRootContext} from './component';
import {baseDirectiveCreate, createLNode, createLViewData, createTView, elementCreate, enterView, hostElement, initChangeDetectorIfExisting, locateHostElement, renderEmbeddedTemplate} from './instructions';
import {ComponentDefInternal, ComponentType, RenderFlags} from './interfaces/definition';
import {LElementNode, TNode, TNodeType} from './interfaces/node';
import {RElement, domRendererFactory3} from './interfaces/renderer';
import {FLAGS, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view';
import {ViewRef} from './view_ref';
export class ComponentFactoryResolver extends viewEngine_ComponentFactoryResolver {
@ -80,23 +80,28 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
}
create(
parentComponentInjector: Injector, projectableNodes?: any[][]|undefined,
rootSelectorOrNode?: any,
injector: Injector, projectableNodes?: any[][]|undefined, rootSelectorOrNode?: any,
ngModule?: viewEngine_NgModuleRef<any>|undefined): viewEngine_ComponentRef<T> {
ngDevMode && assertDefined(ngModule, 'ngModule should always be defined');
const isInternalRootView = rootSelectorOrNode === undefined;
const rendererFactory = ngModule ? ngModule.injector.get(RendererFactory2) : document;
const hostNode = locateHostElement(rendererFactory, rootSelectorOrNode);
const rendererFactory =
ngModule ? ngModule.injector.get(RendererFactory2) : domRendererFactory3;
const hostNode = isInternalRootView ?
elementCreate(
this.selector, rendererFactory.createRenderer(null, this.componentDef.rendererType)) :
locateHostElement(rendererFactory, rootSelectorOrNode);
// The first index of the first selector is the tag name.
const componentTag = this.componentDef.selectors ![0] ![0] as string;
const rootContext: RootContext = ngModule !.injector.get(ROOT_CONTEXT);
const rootContext: RootContext = ngModule && !isInternalRootView ?
ngModule.injector.get(ROOT_CONTEXT) :
createRootContext(requestAnimationFrame.bind(window));
// Create the root view. Uses empty TView and ContentTemplate.
const rootView: LViewData = createLViewData(
rendererFactory.createRenderer(hostNode, this.componentDef.rendererType),
createTView(-1, null, null, null, null), null,
createTView(-1, null, null, null, null), rootContext,
this.componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways);
rootView[INJECTOR] = ngModule && ngModule.injector || null;
@ -116,14 +121,49 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
component = baseDirectiveCreate(0, this.componentDef.factory(), this.componentDef) as T);
initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data !);
// TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
// executed here?
// Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
LifecycleHooksFeature(component, this.componentDef);
// Transform the arrays of native nodes into a LNode structure that can be consumed by the
// projection instruction. This is needed to support the reprojection of these nodes.
if (projectableNodes) {
let index = 0;
const projection: TNode[] = elementNode.tNode.projection = [];
for (let i = 0; i < projectableNodes.length; i++) {
const nodeList = projectableNodes[i];
let firstTNode: TNode|null = null;
let previousTNode: TNode|null = null;
for (let j = 0; j < nodeList.length; j++) {
const lNode =
createLNode(++index, TNodeType.Element, nodeList[j] as RElement, null, null);
if (previousTNode) {
previousTNode.next = lNode.tNode;
} else {
firstTNode = lNode.tNode;
}
previousTNode = lNode.tNode;
}
projection.push(firstTNode !);
}
}
// Execute the template in creation mode only, and then turn off the CreationMode flag
renderEmbeddedTemplate(elementNode, elementNode.data ![TVIEW], component, RenderFlags.Create);
elementNode.data ![FLAGS] &= ~LViewFlags.CreationMode;
} finally {
enterView(oldView, null);
if (rendererFactory.end) rendererFactory.end();
}
// TODO(misko): this is the wrong injector here.
return new ComponentRef(
this.componentType, component, rootView, ngModule !.injector, hostNode !);
const componentRef =
new ComponentRef(this.componentType, component, rootView, injector, hostNode !);
if (isInternalRootView) {
// The host element of the internal root view is attached to the component's host view node
componentRef.hostView._lViewNode !.tNode.child = elementNode.tNode;
}
return componentRef;
}
}
@ -159,6 +199,7 @@ export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
* We might want to think about creating a fake component for the top level? Or overwrite
* detectChanges with a function that calls tickRootContext? */
this.hostView = this.changeDetectorRef = new ViewRef(rootView, instance);
this.hostView._lViewNode = createLNode(-1, TNodeType.View, null, null, null, rootView);
this.injector = injector;
this.location = new ElementRef(hostNode);
this.componentType = componentType;