LP#1775466 Typeahead has allowFreeText option
authorBill Erickson <berickxx@gmail.com>
Sun, 1 Jul 2018 21:01:34 +0000 (17:01 -0400)
committerBill Erickson <berickxx@gmail.com>
Wed, 5 Sep 2018 14:05:23 +0000 (10:05 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/share/typeahead/typeahead.component.html
Open-ILS/src/eg2/src/app/share/typeahead/typeahead.component.ts

index c47430a..879a023 100644 (file)
@@ -13,8 +13,9 @@
     [resultTemplate]="displayTemplate"
     [inputFormatter]="formatDisplayString"
     (click)="click$.next($event.target.value)"
+    (blur)="onBlur()"
     (selectItem)="selectorChanged($event)"
-    #input #instance="ngbTypeahead"/>
+    #instance="ngbTypeahead"/>
   <div class="d-flex flex-column icons" (click)="openMe($event)">
     <span class="material-icons">keyboard_arrow_up</span>
     <span class="material-icons">keyboard_arrow_down</span>
index 98e9047..1c5afac 100644 (file)
@@ -13,7 +13,7 @@ import {StoreService} from '@eg/core/store.service';
 export interface TypeaheadEntry {
   id: any;
   label: string;
-  disabled?: boolean;
+  freetext?: boolean;
 }
 
 @Component({
@@ -29,6 +29,7 @@ export class TypeaheadComponent implements OnInit {
     selected: TypeaheadEntry;
     click$: Subject<string>;
     entrylist: TypeaheadEntry[];
+    freeTextId: number;
 
     @ViewChild('instance') instance: NgbTypeahead;
 
@@ -41,6 +42,8 @@ export class TypeaheadComponent implements OnInit {
     // box regardless of any text that already exists there.
     @Input() clickShowsAll = true;
 
+    @Input() allowFreeText = false;
+
     // Entry ID of the default entry to select (optional)
     // onChange() is NOT fired when applying the default value
     @Input() startId: any;
@@ -63,6 +66,7 @@ export class TypeaheadComponent implements OnInit {
         this.entrylist = [];
         this.click$ = new Subject<string>();
         this.onChange = new EventEmitter<TypeaheadEntry>();
+        this.freeTextId = -1;
 
         this.formatDisplayString = (result: TypeaheadEntry) => {
             return result.label.trim();
@@ -83,11 +87,34 @@ export class TypeaheadComponent implements OnInit {
         setTimeout(() => this.click$.next(''));
     }
 
+    onBlur() {
+
+        if (typeof this.selected === 'string' && this.selected !== '') {
+            // Free text entered which does not match a known entry
+
+            if (this.allowFreeText) {
+                // translate it into a dummy TypeaheadEntry
+                // and manually fire the onchange handler.
+                this.selected = {
+                    id: this.freeTextId--,
+                    label: this.selected,
+                    freetext: true
+                }
+                this.selectorChanged(
+                    {item: this.selected, preventDefault: () => true});
+            } else {
+                // If free text is now allowed, clear the value when
+                // the user navigates away to avoid confusion.
+                this.selected = null;
+            }
+        }
+    }
 
     // Fired by the typeahead to inform us of a change.
-    // TODO: this does not fire when the value is cleared :( -- implement
-    // change detection on this.selected to look specifically for NULL.
+    // This only fires when an item in the list is selected, not when
+    // the value is cleared or free-text is used.
     selectorChanged(selEvent: NgbTypeaheadSelectItemEvent) {
+        console.log('selector changed');
         this.onChange.emit(selEvent.item.id);
     }