|
|
|
@ -1,104 +1,122 @@
|
|
|
|
|
import { Component, Input, forwardRef,ElementRef,ViewChild } from '@angular/core';
|
|
|
|
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR,NgModel } from '@angular/forms';
|
|
|
|
|
import { Observable,of } from 'rxjs';
|
|
|
|
|
import { tap,catchError,debounceTime,distinctUntilChanged,switchMap } from 'rxjs/operators'
|
|
|
|
|
import { TypeaheadService } from '../../services/typeahead.service';
|
|
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
|
selector: 'fm-tag-input',
|
|
|
|
|
templateUrl: 'tag-input.component.html',
|
|
|
|
|
styleUrls: ['tag-input.component.scss'],
|
|
|
|
|
providers: [
|
|
|
|
|
{
|
|
|
|
|
provide: NG_VALUE_ACCESSOR,
|
|
|
|
|
useExisting: forwardRef(() => TagInputComponent),
|
|
|
|
|
multi: true
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export class TagInputComponent implements ControlValueAccessor {
|
|
|
|
|
@Input() tags: string[]
|
|
|
|
|
@ViewChild('taginput', { static: true }) tagInputElement: ElementRef;
|
|
|
|
|
public tag: string;
|
|
|
|
|
searching = false;
|
|
|
|
|
searchFailed = false;
|
|
|
|
|
|
|
|
|
|
constructor(private typeaheadService: TypeaheadService) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tagExists(tag) {
|
|
|
|
|
if (tag.length == 0) return true;
|
|
|
|
|
for (let t of this.tags) {
|
|
|
|
|
if (t.toLowerCase() == tag.toLowerCase()) return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleDeleteTag(tag) {
|
|
|
|
|
let tags = [];
|
|
|
|
|
for (let t of this.tags) {
|
|
|
|
|
if (t != tag) tags.push(t);
|
|
|
|
|
}
|
|
|
|
|
this.tags = tags;
|
|
|
|
|
this.propagateChange(tags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleAddTag(event) {
|
|
|
|
|
if (!this.tagExists(this.tag)) {
|
|
|
|
|
this.tags.push(this.tag);
|
|
|
|
|
this.propagateChange(this.tags);
|
|
|
|
|
}
|
|
|
|
|
this.tag = "";
|
|
|
|
|
this.tagInputElement.nativeElement.focus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleCheckAddTag(event: KeyboardEvent) {
|
|
|
|
|
if (event.keyCode == 188) {
|
|
|
|
|
let tag = this.tag.substr(0, this.tag.length - 1); // strip ,
|
|
|
|
|
if (!this.tagExists(tag)) {
|
|
|
|
|
this.tags.push(tag);
|
|
|
|
|
this.propagateChange(this.tags);
|
|
|
|
|
}
|
|
|
|
|
this.tag = "";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleSelect(event) {
|
|
|
|
|
if (!this.tagExists(event.item)) {
|
|
|
|
|
this.tags.push(event.item);
|
|
|
|
|
this.propagateChange(this.tags);
|
|
|
|
|
}
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
this.tag = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
propagateChange = (_: any) => { };
|
|
|
|
|
|
|
|
|
|
registerOnChange(fn) {
|
|
|
|
|
this.propagateChange = fn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
findTag = (text$: Observable<string>) =>
|
|
|
|
|
text$.pipe(
|
|
|
|
|
debounceTime(200),
|
|
|
|
|
distinctUntilChanged(),
|
|
|
|
|
tap(() => this.searching = true),
|
|
|
|
|
switchMap(term => term.length < 1 ? of([]) :
|
|
|
|
|
this.typeaheadService.getTagTypeaheadItems(term).pipe(
|
|
|
|
|
tap(() => this.searchFailed = false),
|
|
|
|
|
catchError(() => {
|
|
|
|
|
this.searchFailed = true;
|
|
|
|
|
return of([]);
|
|
|
|
|
}))
|
|
|
|
|
),
|
|
|
|
|
tap(() => this.searching = false)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
writeValue(value: any) {
|
|
|
|
|
this.tags = value;
|
|
|
|
|
this.tag = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
registerOnTouched() { }
|
|
|
|
|
}
|
|
|
|
|
import {Component, ElementRef, forwardRef, Input, ViewChild} from '@angular/core';
|
|
|
|
|
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
|
|
|
|
|
import {Observable, of} from 'rxjs';
|
|
|
|
|
import {catchError, debounceTime, distinctUntilChanged, switchMap, tap} from 'rxjs/operators';
|
|
|
|
|
import {TypeaheadService} from '../../services/typeahead.service';
|
|
|
|
|
import {NgbTypeahead} from '@ng-bootstrap/ng-bootstrap';
|
|
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
|
selector: 'fm-tag-input',
|
|
|
|
|
templateUrl: 'tag-input.component.html',
|
|
|
|
|
styleUrls: ['tag-input.component.scss'],
|
|
|
|
|
providers: [
|
|
|
|
|
{
|
|
|
|
|
provide: NG_VALUE_ACCESSOR,
|
|
|
|
|
useExisting: forwardRef(() => TagInputComponent),
|
|
|
|
|
multi: true
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export class TagInputComponent implements ControlValueAccessor {
|
|
|
|
|
@Input() tags: string[] = [];
|
|
|
|
|
@ViewChild('tagInputElement', {static: true}) tagInputElement: ElementRef;
|
|
|
|
|
@ViewChild(NgbTypeahead, {static: true}) typeahead: NgbTypeahead;
|
|
|
|
|
|
|
|
|
|
public tag: string;
|
|
|
|
|
searching = false;
|
|
|
|
|
searchFailed = false;
|
|
|
|
|
|
|
|
|
|
constructor(private typeaheadService: TypeaheadService) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tagExists(tag) {
|
|
|
|
|
if (tag.length === 0) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
for (const t of this.tags) {
|
|
|
|
|
if (t.toLowerCase() === tag.toLowerCase()) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addTag(tag, keepFocus = true) {
|
|
|
|
|
if (!this.tagExists(tag)) {
|
|
|
|
|
this.tags.push(tag);
|
|
|
|
|
this.propagateChange(this.tags);
|
|
|
|
|
}
|
|
|
|
|
this.tag = '';
|
|
|
|
|
|
|
|
|
|
if (keepFocus) {
|
|
|
|
|
this.tagInputElement.nativeElement.focus();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleDeleteTag(tag) {
|
|
|
|
|
const tags = [];
|
|
|
|
|
for (const t of this.tags) {
|
|
|
|
|
if (t !== tag) {
|
|
|
|
|
tags.push(t);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
this.tags = tags;
|
|
|
|
|
this.propagateChange(tags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleBlur(event, keepFocus = true) {
|
|
|
|
|
if (!this.typeahead.isPopupOpen()) {
|
|
|
|
|
this.addTag(this.tag, keepFocus);
|
|
|
|
|
} else {
|
|
|
|
|
this.tag = '';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleKeyUp(event: KeyboardEvent) {
|
|
|
|
|
if (event.keyCode === 188) {
|
|
|
|
|
const tag = this.tag.substr(0, this.tag.length - 1); // strip ,
|
|
|
|
|
this.addTag(tag);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleSelect(event) {
|
|
|
|
|
if (!this.tagExists(event.item)) {
|
|
|
|
|
this.tags.push(event.item);
|
|
|
|
|
this.propagateChange(this.tags);
|
|
|
|
|
}
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
this.tag = '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
propagateChange = (_: any) => {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
registerOnChange(fn) {
|
|
|
|
|
this.propagateChange = fn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
findTag = (text$: Observable<string>) =>
|
|
|
|
|
text$.pipe(
|
|
|
|
|
distinctUntilChanged(),
|
|
|
|
|
debounceTime(200),
|
|
|
|
|
tap(() => this.searching = true),
|
|
|
|
|
switchMap(term => term.length < 1 ? of([]) :
|
|
|
|
|
this.typeaheadService.getTagTypeaheadItems(term).pipe(
|
|
|
|
|
tap(() => this.searchFailed = false),
|
|
|
|
|
catchError(() => {
|
|
|
|
|
this.searchFailed = true;
|
|
|
|
|
return of([]);
|
|
|
|
|
}))
|
|
|
|
|
),
|
|
|
|
|
tap(() => this.searching = false)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
writeValue(value: any) {
|
|
|
|
|
this.tags = value;
|
|
|
|
|
this.tag = '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
registerOnTouched() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|