import { Observable , BehaviorSubject , Subject , asyncScheduler , Subscription } from 'rxjs' ; import { map , distinctUntilChanged , observeOn } from 'rxjs/operators' ; import { RootContainer } from './root_container' ; import { Injectable , Optional , Inject } from '@angular/core' ; let containerId = - 1 ; @ Injectable ( ) export abstract class Container < S extends object > { private _setState$ = new Subject < Partial < S > > ( ) ; private _setStateSubscription : Subscription ; private _state$ = new BehaviorSubject < S > ( Object . assign ( { } , this . getInitialState ( ) ) ) ; private _defaultContainerInstanceId : string = `${ this . _getClassName ( ) }@${ ++ containerId }` ; constructor ( @ Optional ( ) @ Inject ( RootContainer ) private _rootContainer ) { if ( this . _rootContainer ) { this . _rootContainer . registerContainer ( this ) ; } this . _setStateSubscription = this . _subscribeToSetState ( ) ; } /**
     * setState updates the state of the container. The returned Observable completes when the state
     * state updated is completed.
     */ protected setState ( stateFn ) { this . _setState$ . next ( stateFn ( this . _state$ . value ) ) ; } /**
     * Implement this method to describe the initial state of your container.
     */ protected abstract getInitialState ( ) ; final select < K > ( selectFn ) { return this . _state$ . pipe ( map ( state => selectFn ( state as Readonly < S > ) ) , distinctUntilChanged ( ) , observeOn ( asyncScheduler ) ) ; } private _subscribeToSetState ( ) { return this . _setState$ . subscribe ( partialState => { this . _state$ . next ( Object . assign ( { } , this . _state$ . value , partialState ) ) ; } ) ; } /**
     * When you override this method, you have to call the destroy method in your ngOnDestroy method.
     */ protected ngOnDestroy ( ) { this . destroy ( ) ; } final protected destroy ( ) { this . _setStateSubscription . unsubscribe ( ) ; this . _state$ . complete ( ) ; if ( this . _rootContainer ) { this . _rootContainer . unregisterContainer ( this ) ; } } /**
     * You can override this method if you want to give your container instance a custom id.
     * The returned id must be unique in the application.
     */ getContainerInstanceId ( ) { return this . _defaultContainerInstanceId ; } private _getClassName ( ) { return this . constructor . name ; } }