import { Component , Inject , Input , NgModule , NgModuleRef , NgZone , OnChanges , OnDestroy , OnInit , SimpleChanges , ɵresetCompiledComponents as resetCompiledComponents } from '@angular/core' ; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic' ; import { Scenario , SelectedSandboxAndScenarioKeys , Sandbox } from '../../lib/app-state' ; import { BrowserModule } from '@angular/platform-browser' ; import { Middleware , MIDDLEWARE } from '../../lib/middlewares' ; import { takeUntil } from 'rxjs/operators' ; import { Subject } from 'rxjs' ; import { Sandboxes } from "../shared/sandboxes" ; @ Component ( { selector : 'ap-scenario' , template : `
        <ng-template></ng-template>` , } ) export class ScenarioComponent implements OnInit , OnChanges , OnDestroy { /**
     * The selected sandbox and scenario provided from the app dropdown
     */ @ Input ( ) selectedSandboxAndScenarioKeys : SelectedSandboxAndScenarioKeys ; /**
     * Collection of bootstrapped apps
     */ private activeApps : NgModuleRef < any > [ ] = [ ] ; /**
     * Modules that are applied across every sandbox instance
     */ private activeMiddleware : Middleware ; /**
     * Unsubscribe all subscriptions on component destroy
     */ private onDestroy = new Subject < void > ( ) ; constructor ( private zone , @ Inject ( MIDDLEWARE ) private middleware , private sanboxes ) { } ngOnInit ( ) { this . middleware . pipe ( takeUntil ( this . onDestroy ) ) . subscribe ( middlewares => this . activeMiddleware = middlewares ) ; } ngOnChanges ( changes ) { if ( changes . selectedSandboxAndScenarioKeys ) { this . bootstrapSandbox ( changes . selectedSandboxAndScenarioKeys . currentValue ) ; } } ngOnDestroy ( ) { this . onDestroy . next ( ) ; if ( this . activeApps . length > 0 ) { const app = this . activeApps . pop ( ) ; app . destroy ( ) ; const hostElement = document . querySelector ( 'playground-host' ) ; if ( hostElement && hostElement . children ) { hostElement . children [ 0 ] . remove ( ) ; } } } /**
     * Bootstrap a new Angular application with the sandbox's required dependencies
     */ private bootstrapSandbox ( selectedSandboxAndScenarioKeys ) { this . sanboxes . getSandbox ( selectedSandboxAndScenarioKeys . sandboxKey ) . then ( sandbox => { if ( sandbox ) { const scenario = sandbox . scenarios . find ( ( s ) => s . key === selectedSandboxAndScenarioKeys . scenarioKey ) ; if ( scenario ) { if ( this . activeApps . length > 0 ) { const app = this . activeApps . pop ( ) ; app . destroy ( ) ; } this . zone . runOutsideAngular ( ( ) => { const module = this . createModule ( sandbox , scenario ) ; platformBrowserDynamic ( ) . bootstrapModule ( module ) . then ( app => { this . activeApps . push ( app ) ; resetCompiledComponents ( ) ; ( window as any ) . isPlaygroundComponentLoaded = ( ) => true ; } ) . catch ( err => { resetCompiledComponents ( ) ; ( window as any ) . isPlaygroundComponentLoadedWithErrors = ( ) => true ; console . error ( err ) ; } ) ; } ) ; } } } ) ; } /**
     * Create a module containing the dependencies of a sandbox
     */ private createModule ( sandboxMeta , scenario ) { const hostComp = this . createComponent ( scenario ) ; class DynamicModule { ngDoBootstrap ( app ) { const hostEl = document . querySelector ( 'playground-host' ) ; if ( ! hostEl ) { const compEl = document . createElement ( 'playground-host' ) ; document . body . appendChild ( compEl ) ; } app . bootstrap ( hostComp ) ; } } return NgModule ( { imports : [ BrowserModule , ... sandboxMeta . imports , ... this . activeMiddleware . modules , ] , declarations : [ hostComp , sandboxMeta . declareComponent ? sandboxMeta . type : [ ] , ... sandboxMeta . declarations , ] , providers : [ ... sandboxMeta . providers ] , entryComponents : [ hostComp , ... sandboxMeta . entryComponents ] , schemas : [ ... sandboxMeta . schemas ] , } ) ( DynamicModule ) ; } /**
     * Construct a component to serve as the host for the provided scenario
     */ private createComponent ( scenario ) { class DynamicComponent { constructor ( ) { Object . assign ( this , scenario . context ) ; } } return Component ( { selector : 'playground-host' , template : scenario . template , styles : scenario . styles , providers : scenario . providers , } ) ( DynamicComponent ) ; } }