import { ApplicationRef , ComponentFactory , ComponentFactoryResolver , ComponentRef , ElementRef , EventEmitter , Injector , NgZone , Provider , ReflectiveInjector , Renderer2 , TemplateRef , Type , ViewContainerRef } from '@angular/core' ; import { PositioningOptions , PositioningService } from '../positioning' ; import { listenToTriggers } from '../triggers' ; import { ContentRef } from './content-ref.class' ; export interface ListenOptions { target ? : ElementRef ; triggers ? : string ; show ? : Function | any ; hide ? : Function | any ; toggle ? : Function ; } export class ComponentLoader < T > { public onBeforeShow : EventEmitter < any > = new EventEmitter ( ) ; public onShown : EventEmitter < any > = new EventEmitter ( ) ; public onBeforeHide : EventEmitter < any > = new EventEmitter ( ) ; public onHidden : EventEmitter < any > = new EventEmitter ( ) ; public instance : T ; public _componentRef : ComponentRef < T > | any ; private _providers : Provider [ ] = [ ] ; private _componentFactory : ComponentFactory < T > ; private _zoneSubscription : any ; private _contentRef : ContentRef | any ; private _innerComponent : ComponentRef < T > | any ; private _unregisterListenersFn : Function ; public get isShown ( ) : boolean { return ! ! this . _componentRef ; } /**
     * Placement of a component. Accepts: "top", "bottom", "left", "right"
     */ private attachment : string ; /**
     * A selector specifying the element the popover should be appended to.
     * Currently only supports "body".
     */ private container : string | ElementRef | any ; /**
     * Specifies events that should trigger. Supports a space separated list of
     * event names.
     */ private triggers : string ; internal public constructor ( private _viewContainerRef , private _renderer , private _elementRef , private _injector , private _componentFactoryResolver , private _ngZone , private _applicationRef , private _posService ) { } public attach ( compType ) { this . _componentFactory = this . _componentFactoryResolver . resolveComponentFactory < T > ( compType ) ; return this ; } public to ( container ? ) { this . container = container || this . container ; return this ; } public position ( opts ? ) { this . attachment = opts . attachment || this . attachment ; this . _elementRef = opts . target as ElementRef || this . _elementRef ; return this ; } public provide ( provider ) { this . _providers . push ( provider ) ; return this ; } public show ( opts = { } ) { this . _subscribePositioning ( ) ; this . _innerComponent = null ; if ( ! this . _componentRef ) { this . onBeforeShow . emit ( ) ; this . _contentRef = this . _getContentRef ( opts . content ) ; const injector = ReflectiveInjector . resolveAndCreate ( this . _providers , this . _injector ) ; this . _componentRef = this . _componentFactory . create ( injector , this . _contentRef . nodes ) ; this . _applicationRef . attachView ( this . _componentRef . hostView ) ; this . instance = this . _componentRef . instance ; Object . assign ( this . _componentRef . instance , opts ) ; if ( this . container instanceof ElementRef ) { this . container . nativeElement . appendChild ( this . _componentRef . location . nativeElement ) ; } if ( this . container === 'body' && typeof document !== 'undefined' ) { document . querySelector ( this . container as string | any ) . appendChild ( this . _componentRef . location . nativeElement ) ; } if ( ! this . container && this . _elementRef && this . _elementRef . nativeElement . parentElement ) { this . _elementRef . nativeElement . parentElement . appendChild ( this . _componentRef . location . nativeElement ) ; } if ( this . _contentRef . componentRef ) { this . _innerComponent = this . _contentRef . componentRef . instance ; this . _contentRef . componentRef . changeDetectorRef . markForCheck ( ) ; this . _contentRef . componentRef . changeDetectorRef . detectChanges ( ) ; } this . _componentRef . changeDetectorRef . markForCheck ( ) ; this . _componentRef . changeDetectorRef . detectChanges ( ) ; this . onShown . emit ( this . _componentRef . instance ) ; } return this . _componentRef ; } public hide ( ) { if ( ! this . _componentRef ) { return this ; } this . onBeforeHide . emit ( this . _componentRef . instance ) ; const componentEl = this . _componentRef . location . nativeElement ; componentEl . parentNode . removeChild ( componentEl ) ; if ( this . _contentRef . componentRef ) { this . _contentRef . componentRef . destroy ( ) ; } this . _componentRef . destroy ( ) ; if ( this . _viewContainerRef && this . _contentRef . viewRef ) { this . _viewContainerRef . remove ( this . _viewContainerRef . indexOf ( this . _contentRef . viewRef ) ) ; } this . _contentRef = null ; this . _componentRef = null ; this . onHidden . emit ( ) ; return this ; } public toggle ( ) { if ( this . isShown ) { this . hide ( ) ; return ; } this . show ( ) ; } public dispose ( ) { if ( this . isShown ) { this . hide ( ) ; } this . _unsubscribePositioning ( ) ; if ( this . _unregisterListenersFn ) { this . _unregisterListenersFn ( ) ; } } public listen ( listenOpts ) { this . triggers = listenOpts . triggers || this . triggers ; listenOpts . target = listenOpts . target || this . _elementRef ; listenOpts . show = listenOpts . show || ( ( ) => this . show ( ) ) ; listenOpts . hide = listenOpts . hide || ( ( ) => this . hide ( ) ) ; listenOpts . toggle = listenOpts . toggle || ( ( ) => this . isShown ? listenOpts . hide ( ) : listenOpts . show ( ) ) ; this . _unregisterListenersFn = listenToTriggers ( this . _renderer , listenOpts . target . nativeElement , this . triggers , listenOpts . show , listenOpts . hide , listenOpts . toggle ) ; return this ; } public getInnerComponent ( ) { return this . _innerComponent ; } private _subscribePositioning ( ) { if ( this . _zoneSubscription || ! this . attachment ) { return ; } this . _zoneSubscription = this . _ngZone . onStable . subscribe ( ( ) => { if ( ! this . _componentRef ) { return ; } this . _posService . position ( { element : this . _componentRef . location , target : this . _elementRef , attachment : this . attachment , appendToBody : this . container === 'body' } ) ; } ) ; } private _unsubscribePositioning ( ) { if ( ! this . _zoneSubscription ) { return ; } this . _zoneSubscription . unsubscribe ( ) ; this . _zoneSubscription = null ; } private _getContentRef ( content ) { if ( ! content ) { return new ContentRef ( [ ] ) ; } if ( content instanceof TemplateRef ) { if ( this . _viewContainerRef ) { const viewRef = this . _viewContainerRef . createEmbeddedView < TemplateRef < T > > ( content ) ; return new ContentRef ( [ viewRef . rootNodes ] , viewRef ) ; } const viewRef = content . createEmbeddedView ( { } ) ; this . _applicationRef . attachView ( viewRef ) ; return new ContentRef ( [ viewRef . rootNodes ] , viewRef ) ; } if ( typeof content === 'function' ) { const contentCmptFactory = this . _componentFactoryResolver . resolveComponentFactory ( content ) ; const modalContentInjector = ReflectiveInjector . resolveAndCreate ( [ ... this . _providers , content ] , this . _injector ) ; const componentRef = contentCmptFactory . create ( modalContentInjector ) ; this . _applicationRef . attachView ( componentRef . hostView ) ; return new ContentRef ( [ [ componentRef . location . nativeElement ] ] , componentRef . hostView , componentRef ) ; } return new ContentRef ( [ [ this . _renderer . createText ( `${ content }` ) ] ] ) ; } }