/** * Created by gaurav.pandvia on 2/15/17. */ import { ApplicationRef, ComponentFactoryResolver, ComponentRef, Injectable, Injector, ViewContainerRef, EmbeddedViewRef, Type } from '@angular/core'; /** * Injection service is a helper to append components * dynamically to a known location in the DOM, most * noteably for dialogs/tooltips appending to body. * * @export * @class InjectionService */ @Injectable() export class InjectionService { private _container: ComponentRef; constructor( private applicationRef: ApplicationRef, private componentFactoryResolver: ComponentFactoryResolver, private injector: Injector) { } /** * Gets the root view container to inject the component to. * * @returns {ComponentRef} * * @memberOf InjectionService */ getRootViewContainer(): ComponentRef { if(this._container) return this._container; const rootComponents = this.applicationRef['_rootComponents']; if (rootComponents.length) return rootComponents[0]; throw new Error('View Container not found! ngUpgrade needs to manually set this via setRootViewContainer.'); } /** * Overrides the default root view container. This is useful for * things like ngUpgrade that doesn't have a ApplicationRef root. * * @param {any} container * * @memberOf InjectionService */ setRootViewContainer(container): void { this._container = container; } /** * Gets the html element for a component ref. * * @param {ComponentRef} componentRef * @returns {HTMLElement} * * @memberOf InjectionService */ getComponentRootNode(componentRef: any): HTMLElement { // the top most component root node has no `hostView` if(!componentRef.hostView) return componentRef.element.nativeElement; return (componentRef.hostView as EmbeddedViewRef).rootNodes[0] as HTMLElement; } /** * Gets the root component container html element. * * @returns {HTMLElement} * * @memberOf InjectionService */ getRootViewContainerNode(componentRef): HTMLElement { return this.getComponentRootNode(componentRef); } /** * Projects the bindings onto the component * * @param {ComponentRef} component * @param {*} options * @returns {ComponentRef} * * @memberOf InjectionService */ projectComponentBindings(component: ComponentRef, bindings: any): ComponentRef { if(bindings) { if (bindings.inputs !== undefined) { const bindingKeys = Object.getOwnPropertyNames(bindings.inputs); for (const bindingName of bindingKeys) { component.instance[bindingName] = bindings.inputs[bindingName]; } } if (bindings.outputs !== undefined) { const eventKeys = Object.getOwnPropertyNames(bindings.outputs); for (const eventName of eventKeys) { component.instance[eventName] = bindings.outputs[eventName]; } } } return component; } /** * Appends a component to a adjacent location * * @template T * @param {Type} componentClass * @param {*} [options={}] * @param {Element} [location] * @returns {ComponentRef} * * @memberOf InjectionService */ appendComponent( componentClass: any, bindings: any = {}, location?: any): ComponentRef { let vc : ViewContainerRef; const componentFactory = this.componentFactoryResolver.resolveComponentFactory(componentClass); const componentRef = componentFactory.create(this.injector); if(bindings.inputs.hasOwnProperty('viewContainerRef')){ vc = bindings.inputs['viewContainerRef']; } const appRef: any = this.applicationRef; const componentRootNode = this.getComponentRootNode(componentRef); // appRef.attachView(componentRef.hostView); //AttachView function is valid in angular 2.3 and after so can't use let testRef = vc.createComponent(componentFactory); // project the options passed to the component instance this.projectComponentBindings(testRef, bindings); componentRef.onDestroy(() => { testRef.destroy(); }); // location override not passed, get `this._container` // if(!location) location = this.getRootViewContainer(); // // const appendLocation = this.getComponentRootNode(location); // appendLocation.appendChild(componentRootNode); return testRef; } }