import { Component , OnInit , Input , ElementRef , HostBinding , ApplicationRef , ComponentFactoryResolver , Injector , ViewContainerRef , NgZone , Output , EventEmitter , OnDestroy , Type , ChangeDetectorRef , ChangeDetectionStrategy } from '@angular/core' ; import { HttpClient , HttpErrorResponse } from '@angular/common/http' ; import { ComponentPortal } from '@angular/cdk/portal' ; import { ExampleViewerComponent } from '../example-viewer/example-viewer.component' ; import { take } from 'rxjs/operators' ; import { DomPortalOutlet } from '../../services/dom-portal-outlet' ; import { getBuiltInComponents } from '../../built-in/built-in-components' ; import { ContentRenderer } from '../content-renderer' ; import { TocService } from '../../services/toc.service' ; @ Component ( { selector : 'dg-content-viewer' , template : 'Loading...' , changeDetection : ChangeDetectionStrategy . OnPush } ) export class ContentViewerComponent extends ContentRenderer implements OnInit , OnDestroy { @ HostBinding ( 'class.dg-doc-content' ) isDocContent = true ; @ Output ( ) contentRendered = new EventEmitter < HTMLElement > ( ) ; private portalHosts : DomPortalOutlet [ ] = [ ] ; constructor ( http , public elementRef , private appRef , private componentFactoryResolver , private injector , private viewContainerRef , private ngZone , private tocService , private cdr ) { super ( http ) ; } ngOnInit ( ) { } updateDocument ( content ) { this . elementRef . nativeElement . innerHTML = content ; this . loadComponents ( 'example' , ExampleViewerComponent ) ; getBuiltInComponents ( ) . forEach ( item => { this . loadComponents ( item . selector , item . component ) ; } ) ; this . cdr . markForCheck ( ) ; this . ngZone . onStable . pipe ( take ( 1 ) ) . subscribe ( ( ) => { this . ngZone . run ( ( ) => { this . contentRendered . emit ( this . elementRef . nativeElement ) ; this . updateTableOfContents ( this . elementRef . nativeElement ) ; } ) ; } ) ; } updateTableOfContents ( docViewerContent ) { if ( docViewerContent ) { this . tocService . generateToc ( docViewerContent ) ; } } private loadComponents ( selector , componentClass ) { const exampleElements = this . elementRef . nativeElement . querySelectorAll ( selector ) ; Array . prototype . slice . call ( exampleElements ) . forEach ( ( element ) => { const portalHost = new DomPortalOutlet ( element , this . componentFactoryResolver , this . appRef , this . injector , [ element . childNodes as any ] ) ; const examplePortal = new ComponentPortal ( componentClass , this . viewContainerRef ) ; const exampleViewerRef = portalHost . attach ( examplePortal ) ; for ( const attributeKey in element . attributes ) { if ( Object . prototype . hasOwnProperty . call ( element . attributes , attributeKey ) ) { const attribute = element . attributes [ attributeKey ] ; const setAttributeFn = exampleViewerRef . instance [ 'setAttribute' ] ; if ( setAttributeFn ) { setAttributeFn . call ( exampleViewerRef . instance , attribute . nodeName , element . getAttribute ( attribute . nodeName ) ) ; } else { exampleViewerRef . instance [ attribute . nodeName ] = element . getAttribute ( attribute . nodeName ) ; } } } this . portalHosts . push ( portalHost ) ; } ) ; } /** Show an error that occurred when fetching a document. */ showError ( url , error ) { console . log ( error ) ; this . elementRef . nativeElement . innerText = `Failed to load document: ${ url }. Error: ${ error . statusText }` ; } private clearLiveExamples ( ) { this . portalHosts . forEach ( h => h . dispose ( ) ) ; this . portalHosts = [ ] ; } ngOnDestroy ( ) { this . clearLiveExamples ( ) ; this . tocService . reset ( ) ; super . destroy ( ) ; } }