import { Component , ElementRef , ViewChild } from '@angular/core' ; import { Observable , Subject } from 'rxjs' ; import { concatMap , exhaustMap , mergeMap , switchMap , endWith , ignoreElements , scan , startWith , tap } from 'rxjs/operators' ; import { addImg } from './addImg' ; interface Results { clicks : number ; dropped : number ; completed : number ; } @ Component ( { templateUrl : './mapping.component.html' } ) export class MappingComponent { @ ViewChild ( 'svg' ) svg : ElementRef ; /** Subject/Observable of clicks of the drop button */ click$ = new Subject ( ) ; /** Name of the map operator to use. */ mapOpName = 'mergeMap' ; /**
     * Observable of results from clicking the drop button given the current map operator.
     * Emits {clicks, dropped, completed} counts built from combining clicks with
     * animation observables, created and flattened by the selected map operator.
     */ results$ : Observable < Results > ; constructor ( ) { this . reset ( ) ; } /** Reset the results$ observable. */ reset ( ) { const mapOperator = this . mapOpName === 'exhaustMap' ? exhaustMap : this . mapOpName === 'concatMap' ? concatMap : this . mapOpName === 'switchMap' ? switchMap : mergeMap ; const animation = ( ) => addImg ( this . svg . nativeElement ) . pipe ( ignoreElements ( ) , startWith ( 'START' ) , endWith ( 'END' ) ) ; const results = { clicks : 0 , dropped : 0 , completed : 0 } ; this . results$ = this . click$ . pipe ( tap ( ( ) => results . clicks ++ ) , mapOperator ( animation ) , scan ( ( state , action ) => { if ( action === 'START' ) { state . dropped ++ ; } else if ( action === 'END' ) { state . completed ++ ; } return state ; } , results ) , startWith ( results ) ) ; } }