import { Component , EventEmitter , Input , OnDestroy , OnInit , Output , ViewChild } from '@angular/core' ; import { CmdbType } from '../../../framework/models/cmdb-type' ; import { BehaviorSubject , Observable , ReplaySubject , Subject } from 'rxjs' ; import { FormControl , FormGroup , Validators } from '@angular/forms' ; import { TypeService } from '../../../framework/services/type.service' ; import { takeUntil } from 'rxjs/operators' ; import { APIGetMultiResponse } from '../../../services/models/api-response' ; import { CollectionParameters } from '../../../services/models/api-parameter' ; import { NgSelectComponent } from '@ng-select/ng-select' ; import { ToastService } from '../../toast/toast.service' ; @ Component ( { selector : 'cmdb-type-select' , templateUrl : './type-select.component.html' , styleUrls : [ './type-select.component.scss' ] } ) export class TypeSelectComponent < T = CmdbType > implements OnInit , OnDestroy { /**
     * NG select component.
     */ @ ViewChild ( NgSelectComponent , { static : true } ) ngSelect ! : NgSelectComponent ; private private subscriber : ReplaySubject < void > = new ReplaySubject < void > ( ) ; /**
     * Ng selected item changed.
     */ @ Output ( ) public selectedChange : EventEmitter < T > = new EventEmitter < T > ( ) ; /**
     * New value was set.
     */ @ Output ( ) public valueChange : EventEmitter < any > = new EventEmitter < any > ( ) ; /**
     * Type control.
     */ public typeControl : FormControl = new FormControl ( undefined , Validators . required ) ; @ Input ( 'typeControl' ) public set TypeControl ( control ) { this . typeControl = control ; this . typeControl . valueChanges . pipe ( takeUntil ( this . subscriber ) ) . subscribe ( changes => { this . valueChange . emit ( changes ) ; } ) ; } /**
     * Reactive form group.
     */ public formGroup : FormGroup ; @ Input ( 'formGroup' ) public set FormGroup ( group ) { this . formGroup = group ; if ( ! this . formGroup . contains ( 'type_id' ) ) { this . formGroup . addControl ( 'type_id' , this . typeControl ) ; } } /**
     * Search function enabled
     */ @ Input ( ) public searchable : boolean = true ; /**
     * Virtual scroll enabled
     */ @ Input ( ) public virtualScroll : boolean = true ; /**
     * Input text placeholder
     */ @ Input ( ) public placeHolderText : string = 'Select a type' ; /**
     * Ng select label
     */ public readonly bindLabel : string = 'label' ; /**
     * Ng select value
     */ public readonly bindValue : string = 'public_id' ; /**
     * Ng select clearable
     */ @ Input ( ) public clearable : boolean = true ; /**
     * Types loading right now.
     */ @ Input ( ) public loading : boolean = false ; /**
     * Preload first 10 types.
     */ @ Input ( ) public preload : boolean = true ; /**
     * Auto load types inside component.
     */ @ Input ( ) public autoLoad : boolean = true ; /**
     * Emit new loading indicator.
     * Will only send if autoLoad is true.
     */ @ Output ( ) public loadingStatusEmitter : EventEmitter < void > = new EventEmitter < void > ( ) ; /**
     * Already loaded types.
     */ @ Input ( ) public types : Array < CmdbType > = [ ] ; /**
     * Total number of types
     */ @ Input ( ) public totalTypes : number = 0 ; /**
     * Max number of pages.
     */ @ Input ( ) public maxPages : number = 0 ; /**
     * Current loading page.
     */ @ Input ( ) public currentPage : number = 0 ; constructor ( private typeService , private toast ) { this . formGroup = new FormGroup ( { type : new FormControl ( '' ) } ) ; } param page public static getApiParameters ( page = 1 ) { return { filter : undefined , limit : 10 , sort : 'public_id' , order : 1 , page } as CollectionParameters ; } param publicID public loadDisplayType ( publicID ) { const foundType = this . types . find ( f => f . public_id === publicID ) ; if ( foundType ) { return new BehaviorSubject < CmdbType > ( foundType ) . asObservable ( ) ; } return this . typeService . getType ( publicID ) . pipe ( takeUntil ( this . subscriber ) ) ; } /**
     * Autoload on component init.
     * Triggers the autoload if no types were passed and preload is true.
     * Subscribes to the event emitters.
     */ public ngOnInit ( ) { if ( this . types . length === 0 && this . preload ) { this . triggerAPICall ( ) ; } } /**
     * Increase the current page until max page and call loading the types.
     */ public triggerAPICall ( ) { if ( this . currentPage <= this . maxPages ) { this . currentPage += 1 ; this . loadTypesFromApi ( ) ; } } private private loadTypesFromApi ( ) { this . loading = true ; this . typeService . getTypes ( TypeSelectComponent . getApiParameters ( this . currentPage ) ) . pipe ( takeUntil ( this . subscriber ) ) . subscribe ( ( apiResponse ) => { this . types = [ ... this . types , ... apiResponse . results as Array < CmdbType > ] ; this . totalTypes = apiResponse . total ; this . maxPages = apiResponse . pager . total_pages ; } , ( error ) => this . toast . error ( error ) ) . add ( ( ) => this . loading = false ) ; } param value param item public searchRef ( value , item ) { const term = value . toLocaleLowerCase ( ) ; const line = item . label + item . name + item . public_id ; return line . toLocaleLowerCase ( ) . indexOf ( term ) > - 1 || line . toLocaleLowerCase ( ) . includes ( term ) ; } param term param items public onCustomSearch ( term , items ) { for ( const obj of this . types ) { if ( this . searchRef ( term , obj ) ) { items = [ ... [ obj ] ] ; } else { this . onScrollEnd ( ) ; } } return items ; } /**
     * Triggers type loading or emits status loading when scrolled to the end.
     */ public onScrollEnd ( ) { if ( this . autoLoad ) { this . triggerAPICall ( ) ; } else { this . loadingStatusEmitter . emit ( ) ; } } /**
     * Un subscribe all subscriptions.
     */ public ngOnDestroy ( ) { this . subscriber . next ( ) ; this . subscriber . complete ( ) ; } }