import { Component , EventEmitter , Input , OnChanges , OnDestroy , OnInit , Output , SimpleChanges } from '@angular/core' ; import { FormArray , FormControl , FormGroup , Validators } from '@angular/forms' ; import { ReplaySubject , Subscription } from 'rxjs' ; import { CmdbMode } from '../../modes.enum' ; import { CmdbCategory } from '../../models/cmdb-category' ; import { CmdbType } from '../../models/cmdb-type' ; import { CategoryService , checkCategoryExistsValidator } from '../../services/category.service' ; import { DndDropEvent , DropEffect } from 'ngx-drag-drop' ; import { CollectionParameters } from '../../../services/models/api-parameter' ; import { takeUntil } from 'rxjs/operators' ; import { APIGetMultiResponse } from '../../../services/models/api-response' ; import { ToastService } from '../../../layout/toast/toast.service' ; @ Component ( { selector : 'cmdb-category-form' , templateUrl : './category-form.component.html' , styleUrls : [ './category-form.component.scss' ] } ) export class CategoryFormComponent implements OnInit , OnChanges , OnDestroy { /**
     * Modification mode for the form and validation.
     * Default is always: CREATE
     */ @ Input ( ) public mode : CmdbMode = CmdbMode . Create ; /**
     * Preset data from existing category.
     * Will be ignored if mode is CREATE.
     */ @ Input ( ) public category : CmdbCategory ; /**
     * Inner category holder for assignments
     */ private $category : CmdbCategory = new CmdbCategory ( ) ; /**
     * Event call if form is valid and submit button was pressed.
     * Data will be a category instance.
     */ @ Output ( ) public submitEmitter : EventEmitter < CmdbCategory > = new EventEmitter < CmdbCategory > ( ) ; /**
     * Event which will show the current validation status of the form.
     */ @ Output ( ) public validationEmitter : EventEmitter < boolean > = new EventEmitter < boolean > ( ) ; /**
     * Subscription if any value of the form has changed.
     * Triggers the validationEmitter output.
     */ private valueChangeSubscription : Subscription = new Subscription ( ) ; /**
     * Subscription for the complete category list.
     */ private categoryServiceSubscription : Subscription = new Subscription ( ) ; /**
     * Complete category list. Will be used to select the parent id.
     */ public categories : CmdbCategory [ ] = [ ] ; /**
     * Total number of categories.
     */ public totalCategories : number = 0 ; /**
     * Total number of category pages.
     */ public totalCategoriesPages : number = 0 ; /**
     * Category add form.
     */ public categoryForm : FormGroup ; /**
     * Are categories loading?
     */ public categoriesLoading : boolean = false ; /**
     * Category params
     */ public categoryParams : CollectionParameters = { filter : undefined , limit : 10 , sort : 'public_id' , order : 1 , page : 1 } ; public subscriber : ReplaySubject < void > = new ReplaySubject < void > ( ) ; @ Input ( ) public unAssignedTypes : CmdbType [ ] = [ ] ; private tempAssignedTypes : CmdbType [ ] = [ ] ; @ Input ( ) public set assignedTypes ( value ) { this . tempAssignedTypes = value ; } public get assignedTypes ( ) : CmdbType [ ] { return this . tempAssignedTypes ; } public effect : DropEffect = 'move' ; public readonly fallBackIcon = 'far fa-folder-open' ; /**
     * Category form constructor - Inits the category form
     */ public constructor ( private categoryService , private toast ) { this . categoryForm = new FormGroup ( { name : new FormControl ( '' , Validators . required ) , label : new FormControl ( '' ) , meta : new FormGroup ( { icon : new FormControl ( null ) , order : new FormControl ( null ) } ) , parent : new FormControl ( null ) , types : new FormArray ( [ ] ) } ) ; } public ngOnInit ( ) { if ( CmdbMode . Create === this . mode ) { this . name . setAsyncValidators ( checkCategoryExistsValidator ( this . categoryService ) ) ; } else if ( CmdbMode . Edit === this . mode ) { this . name . disable ( { onlySelf : true } ) ; } this . valueChangeSubscription = this . categoryForm . statusChanges . subscribe ( ( ) => { this . validationEmitter . emit ( this . categoryForm . valid ) ; } ) ; this . categoriesLoading = true ; this . categoryService . getCategories ( this . categoryParams ) . pipe ( takeUntil ( this . subscriber ) ) . subscribe ( ( apiResponse ) => { this . categories = apiResponse . results as Array < CmdbCategory > ; this . totalCategories = apiResponse . total ; this . totalCategoriesPages = apiResponse . pager . total_pages ; this . categoriesLoading = false ; } , ( err ) => this . toast . error ( err ) ) . add ( ( ) => this . categoriesLoading = false ) ; } private loadCategoriesFromAPI ( ) { this . categoriesLoading = true ; this . categoryService . getCategories ( this . categoryParams ) . pipe ( takeUntil ( this . subscriber ) ) . subscribe ( ( apiResponse ) => { this . categories = this . categories . concat ( apiResponse . results as Array < CmdbCategory > ) ; this . categoriesLoading = false ; } , ( err ) => this . toast . error ( err ) ) . add ( ( ) => this . categoriesLoading = false ) ; } public ngOnChanges ( changes ) { if ( changes . category !== undefined && changes . category . currentValue !== undefined && ( changes . category . previousValue !== changes . category . currentValue ) ) { this . $category = this . category ; this . categoryForm . patchValue ( this . $category ) ; for ( const type of this . $category . types ) { this . types . push ( new FormControl ( type ) ) ; } } if ( changes . assignedTypes !== undefined && changes . assignedTypes . currentValue !== undefined && this . $category . types && ( changes . assignedTypes . previousValue !== changes . assignedTypes . currentValue ) ) { const buffer = [ ] ; for ( const type of this . $category . types ) { const assignedType = this . findAssignedTypeByIndex ( type ) ; if ( assignedType ) { buffer . push ( assignedType ) ; } } this . assignedTypes = buffer ; } } /**
     * Load more groups until end of list is reached.
     */ public onScrollToEnd ( ) { if ( this . categoryParams . page < this . totalCategoriesPages ) { this . categoryParams . page += 1 ; this . loadCategoriesFromAPI ( ) ; } } public ngOnDestroy ( ) { this . valueChangeSubscription . unsubscribe ( ) ; this . categoryServiceSubscription . unsubscribe ( ) ; this . submitEmitter . unsubscribe ( ) ; this . subscriber . next ( ) ; this . subscriber . complete ( ) ; } public get name ( ) : FormControl { return this . categoryForm . get ( 'name' ) as FormControl ; } public get label ( ) : FormControl { return this . categoryForm . get ( 'label' ) as FormControl ; } public get meta ( ) : FormGroup { return this . categoryForm . get ( 'meta' ) as FormGroup ; } public get icon ( ) : FormControl { return this . meta . get ( 'icon' ) as FormControl ; } public onIconSelect ( value ) { this . icon . setValue ( value ) ; } public get parent ( ) : FormControl { return this . categoryForm . get ( 'parent' ) as FormControl ; } public get types ( ) : FormArray { return this . categoryForm . get ( 'types' ) as FormArray ; } public findAssignedTypeByIndex ( idx ) { return this . assignedTypes [ this . assignedTypes . findIndex ( x => x . public_id === idx ) ] ; } public clickRemoveAssignedType ( item ) { const index = this . assignedTypes . indexOf ( item ) ; this . assignedTypes . splice ( index , 1 ) ; this . unAssignedTypes . push ( item ) ; this . types . removeAt ( index ) ; } public onDragged ( item , list , effect , control = false ) { if ( effect === 'move' ) { const index = list . indexOf ( item ) ; list . splice ( index , 1 ) ; if ( control ) { this . types . removeAt ( index ) ; } } } public onDrop ( event , list ? , control = false ) { let index = event . index ; if ( typeof index === 'undefined' ) { index = list . length ; } list . splice ( index , 0 , event . data ) ; if ( control ) { this . types . insert ( index , new FormControl ( event . data . public_id ) ) ; } } public onSubmit ( ) { this . categoryForm . markAllAsTouched ( ) ; if ( this . categoryForm . valid ) { this . $category = Object . assign ( this . $category , this . categoryForm . getRawValue ( ) as CmdbCategory ) ; this . submitEmitter . emit ( this . $category ) ; } } }