argos/dmp-frontend/src/app/shared/components/autocompletes/single/single-auto-complete.compon...

121 lines
3.8 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 } 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 { SingleAutoCompleteConfiguration } from './single-auto-complete-configuration';
@Component({
selector: 'app-single-auto-complete',
templateUrl: './single-auto-complete.component.html',
styleUrls: ['./single-auto-complete.component.scss']
})
export class SingleAutoCompleteComponent implements OnInit {
@Input() reactiveFormControl: FormControl;
@Input() placeholder: string;
@Input() validationErrorString: string;
@Input() configuration: SingleAutoCompleteConfiguration;
// Selected Option Event
@Output() optionSelected: EventEmitter<any> = new EventEmitter();
private requestDelay = 200; //ms
private minFilteringChars = 3;
private loadDataOnStart = true;
// @Input() items: Observable<any[]>;
// @Input() filterFn?: (searchQuery: string) => Observable<any[]>;
// @Input() displayFn?: (item: any) => string;
// @Input() titleFn?: (item: any) => string;
// @Input() subtitleFn?: (item: any) => string;
loading = false;
_items: Observable<any[]>;
constructor() {
}
ngOnInit() {
}
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.items || Observable.of([]);
} else if (query && query.length >= this.minFilteringChars) {
if (this.configuration.filterFn) {
return this.configuration.filterFn(query);
} else {
return this.configuration.items || 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;
}
_optionSelected(event: MatAutocompleteSelectedEvent) {
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.reactiveFormControl.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;
}
}