Created
November 11, 2017 13:30
-
-
Save pglazkov/ecc917a2a96cf009a6059fb6160b627c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
ApplicationRef, Componenimport { | |
ApplicationRef, ComponentFactoryResolver, ComponentRef, | |
Injectable, Injector, Type, ViewContainerRef | |
} from '@angular/core'; | |
import { | |
ComponentType, | |
Portal, | |
ComponentPortal, | |
DomPortalHost | |
} from '@angular/cdk/portal'; | |
@Injectable() | |
export class ComponentFactoryService { | |
constructor( | |
private cfr: ComponentFactoryResolver, | |
private defaultInjector: Injector, | |
private appRef: ApplicationRef) { | |
} | |
/** | |
* Instantiates a component and adds it to the DOM. | |
* @constructor | |
* @param {Type<T>} componentType - Type of the component to create, e.g. "DynamicComponent". | |
* @param {HTMLElement | ViewContainerRef} location - (Optional) Location where to inject the | |
* component in the DOM, can be an arbitrary HTML element or a ViewContainerRef. | |
* @param {Injector} injector - (Optional) Injector that should be used as a parent injector | |
* for the component. This is useful only if you want to inject into the component services | |
* that are provided in a different place from where ComponentFactoryService is provided. | |
*/ | |
createComponent<T>( | |
componentType: Type<T>, | |
location?: HTMLElement | ViewContainerRef, | |
injector?: Injector): ComponentRef<T> { | |
// The location where the component should live in Angular's logical component tree, where | |
// it will be part of change detection, etc. | |
// This is different from where the component renders, which is determined by the PortalHost. | |
let logicalLocation: ViewContainerRef | undefined = undefined; | |
// Host element for rendering the component. By default, use "body". | |
let domLocation: HTMLElement = document.body; | |
if (location) { | |
if (location instanceof HTMLElement) { | |
// The location was provided as an arbitrary element and we don't | |
// know if it is part of the Angular app, so leave it "undefined" | |
// and let the PortalHost take care of attaching it to the Angular app. | |
logicalLocation = undefined; | |
domLocation = location; | |
} | |
else { | |
// Location is ViewContainerRef, so use it as the logical tree location as well as actuall | |
// render location. | |
logicalLocation = location; | |
domLocation = location.element.nativeElement; | |
} | |
} | |
// Create a Portal based on the given component type | |
let componentPortal = new ComponentPortal(componentType, logicalLocation, injector); | |
// Create a PortalHost with the specified location as its anchor element | |
let bodyPortalHost = new DomPortalHost( | |
domLocation, | |
this.cfr, | |
this.appRef, | |
injector || this.defaultInjector); | |
// Attach the Portal to the PortalHost. This will instantiate the component and add | |
// it to the DOM. We get a ComponentRef back that we can use to access the component | |
// instance as well as destroy the component after we're done with it. | |
let componentRef = bodyPortalHost.attach(componentPortal) as ComponentRef<T>; | |
return componentRef; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment