argos/dmp-frontend/src/app/shared/components/autocompletes/multiple/multiple-auto-complete.comp...

166 lines
5.3 KiB
TypeScript

import { Input, OnInit, Component, Output, EventEmitter, ViewChild, ElementRef, QueryList, AfterViewInit } from '@angular/core';
import { FormControl, FormGroupDirective, NgForm, FormArray, AbstractControl, FormGroup, FormBuilder, Validators } from '@angular/forms';
import { ErrorStateMatcher, MatChipInputEvent, MatAutocompleteSelectedEvent, MatChipList, MatAutocompleteTrigger } from '@angular/material';
import { TranslateService } from '@ngx-translate/core';
import { Subject, Observable } from 'rxjs';
import { map, startWith, merge, mapTo, mergeMap, timeout } from 'rxjs/operators';
import { MultipleAutoCompleteConfiguration } from './multiple-auto-complete-configuration';
import { ENTER } from '@angular/cdk/keycodes';
import { COMMA } from '@angular/cdk/keycodes';
@Component({
selector: 'app-multiple-auto-complete',
templateUrl: './multiple-auto-complete.component.html',
styleUrls: ['./multiple-auto-complete.component.scss']
})
export class MultipleAutoCompleteComponent implements OnInit {
@ViewChild('textInput') textInput: ElementRef;
@ViewChild(MatAutocompleteTrigger) autocomplete: MatAutocompleteTrigger;
@Input() reactiveFormControl: FormControl;
@Input() placeholder: string;
@Input() validationErrorString: string;
@Input() configuration: MultipleAutoCompleteConfiguration;
@Input() required: boolean;
// Selected Option Event
@Output() optionSelected: EventEmitter<any> = new EventEmitter();
private requestDelay = 200; //ms
private minFilteringChars = 3;
private loadDataOnStart = true;
loading = false;
_items: Observable<any[]>;
visible = true;
selectable = true;
removable = true;
addOnBlur = false;
separatorKeysCodes = [ENTER, COMMA];
inputFormControl: FormControl;
constructor() {
}
ngOnInit() {
this.inputFormControl = new FormControl({ value: this.reactiveFormControl.value, disabled: this.reactiveFormControl.disabled });
}
filter(query: string): Observable<any[]> {
// If loadDataOnStart is enabled and query is empty we return the initial items.
if (this.isNullOrEmpty(query) && this.loadDataOnStart) {
return this.configuration.initialItems(this.reactiveFormControl.value || []) || Observable.of([]);
} else if (query && query.length >= this.minFilteringChars) {
if (this.configuration.filterFn) {
return this.configuration.filterFn(query, this.reactiveFormControl.value || []);
} else {
return this.configuration.initialItems(this.reactiveFormControl.value || []) || Observable.of([]);
}
} else {
return Observable.of([]);
}
}
isNullOrEmpty(query: string): boolean {
return typeof query !== 'string' || query === null || query.length === 0;
}
_displayFn(item: any): string {
if (this.configuration.displayFn && item) { return this.configuration.displayFn(item); }
return item;
}
_titleFn(item: any): string {
if (this.configuration.titleFn && item) { return this.configuration.titleFn(item); }
return item;
}
_subtitleFn(item: any): string {
if (this.configuration.subtitleFn && item) { return this.configuration.subtitleFn(item); }
return null;
}
_requestDelay(): number {
return this.configuration.requestDelay || this.requestDelay;
}
_minFilteringChars(): number {
return this.configuration.minFilteringChars || this.minFilteringChars;
}
_loadDataOnStart(): boolean {
return this.configuration.loadDataOnStart || this.loadDataOnStart;
}
_chipItems(): any[] {
return this.reactiveFormControl.value || [];
}
_optionSelected(event: MatAutocompleteSelectedEvent) {
const newValue = this.reactiveFormControl.value || [];
newValue.push(event.option.value);
this.reactiveFormControl.patchValue(newValue);
this.textInput.nativeElement.value = '';
this.inputFormControl.setValue(null);
this.optionSelected.emit(event.option.value);
}
_onInputFocus() {
// We set the items observable on focus to avoid the request being executed on component load.
if (!this._items) {
this._items = this.inputFormControl.valueChanges
.startWith(null)
.debounceTime(this.requestDelay)
.distinctUntilChanged()
.do(() => { this.loading = true; })
.flatMap(query => {
// If its a valid object, a selection just made and the object is set as the value of the form control. That means we should fire an extra request to the server.
if (this._isValidObject(query)) { return Observable.of([]); }
return this.filter(query);
})
.do(() => { this.loading = false; });
}
}
_isValidObject(value: any): boolean {
try {
if (!value) { return false; }
if (typeof value !== 'object') { JSON.parse(value); }
} catch (e) {
return false;
}
return true;
}
_removeSelectedItem(item: any): void {
let autocompleteValue = this.reactiveFormControl.value;
const index = autocompleteValue.indexOf(item);
if (index >= 0) {
autocompleteValue.splice(index, 1);
this.reactiveFormControl.patchValue(autocompleteValue)
}
}
_onInputClick(item: any) {
if (!this.autocomplete.panelOpen) {
this.autocomplete.openPanel();
}
}
_addItem(event: MatChipInputEvent): void {
// const input = event.input;
// const value = event.value;
// // Add our fruit
// if ((value || '').trim()) {
// this.selectedItems.push(value.trim());
// }
// // Reset the input value
// if (input) {
// input.value = '';
// }
// this.inputFormControl.setValue(null);
}
}