From 5d9ce17d7d1dd8c610a6d3f0c62c4680a0ab51f8 Mon Sep 17 00:00:00 2001 From: Jason Etheridge Date: Wed, 5 Jun 2019 11:42:29 -0400 Subject: [PATCH] LP#1831785: eg-combobox support automatic pcrud-based IDL data sources This patch adds new idlClass and idlField attributes to eg-combobox to enable it to automatically construct a pcrud-base data source. The idlClass property specifies which table/class to use as the base data source, while idlField specifies the label to display. If idlField is not supplied, the label field defaults to "name". It also adds an asyncSupportsEmptyTermClick option to specify that an async data source (whether or not it is automatically built) is expected to never return more than a couple hundred entries or so; when supplied, it will allow fetching the entire contents of the data source when the user clicks on the drop-down. To test ------- [1] Apply the patch and exercise the comboboxes on the Angular sandbox page (/eg2/en-US/staff/sandbox) Sponsored-by: MassLNC Sponsored-by: Georgia Public Library Service Sponsored-by: Indiana State Library Sponsored-by: CW MARS Sponsored-by: King County Library System Signed-off-by: Jason Etheridge Signed-off-by: Galen Charlton Signed-off-by: Bill Erickson Signed-off-by: Jane Sandberg --- .../app/share/combobox/combobox.component.ts | 42 +++++++++++++++---- .../app/staff/sandbox/sandbox.component.html | 24 ++++++++++- 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.ts b/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.ts index 63f964ef81..4a98fe39f6 100644 --- a/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.ts +++ b/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.ts @@ -8,6 +8,7 @@ import {Observable, of, Subject} from 'rxjs'; import {map, tap, reduce, mergeMap, mapTo, debounceTime, distinctUntilChanged, merge, filter} from 'rxjs/operators'; import {NgbTypeahead, NgbTypeaheadSelectItemEvent} from '@ng-bootstrap/ng-bootstrap'; import {StoreService} from '@eg/core/store.service'; +import {PcrudService} from '@eg/core/pcrud.service'; export interface ComboboxEntry { id: any; @@ -61,8 +62,16 @@ export class ComboboxComponent implements OnInit { @Input() startId: any; @Input() startIdFiresOnChange: boolean; + @Input() idlClass: string; + @Input() idlField: string; @Input() asyncDataSource: (term: string) => Observable; + // If true, an async data search is allowed to fetch all + // values when given an empty term. This should be used only + // if the maximum number of entries returned by the data source + // is known to be no more than a couple hundred. + @Input() asyncSupportsEmptyTermClick: boolean; + // Useful for efficiently preventing duplicate async entries asyncIds: {[idx: string]: boolean}; @@ -94,6 +103,7 @@ export class ComboboxComponent implements OnInit { constructor( private elm: ElementRef, private store: StoreService, + private pcrud: PcrudService, ) { this.entrylist = []; this.asyncIds = {}; @@ -108,6 +118,18 @@ export class ComboboxComponent implements OnInit { } ngOnInit() { + if (this.idlClass) { + this.asyncDataSource = term => { + const field = this.idlField || 'name'; + const args = {}; + const extra_args = { order_by : {} }; + args[field] = { 'ilike': `%${term}%`}; // could -or search on label + extra_args['order_by'][this.idlClass] = this.idlField || 'name'; + return this.pcrud.search(this.idlClass, args, extra_args).pipe(map(data => { + return {id: data.id(), label: data[field]()}; + })); + }; + } } openMe($event) { @@ -197,12 +219,19 @@ export class ComboboxComponent implements OnInit { return of(term); } + let searchTerm: string; + searchTerm = term; + if (searchTerm === '_CLICK_' && this.asyncSupportsEmptyTermClick) { + searchTerm = ''; + } else { + } + return new Observable(observer => { - this.asyncDataSource(term).subscribe( + this.asyncDataSource(searchTerm).subscribe( (entry: ComboboxEntry) => this.addAsyncEntry(entry), err => {}, () => { - observer.next(term); + observer.next(searchTerm); observer.complete(); } ); @@ -220,7 +249,7 @@ export class ComboboxComponent implements OnInit { // action is a user click instead of a text entry. // This tells the filter to show all values in sync mode. this.click$.pipe(filter(() => - !this.instance.isPopupOpen() && !this.asyncDataSource + !this.instance.isPopupOpen() )).pipe(mapTo('_CLICK_')) ), @@ -229,12 +258,7 @@ export class ComboboxComponent implements OnInit { map((term: string) => { if (term === '' || term === '_CLICK_') { - // Avoid displaying the existing entries on-click - // for async sources, becuase that implies we have - // the full data set. (setting?) - if (this.asyncDataSource) { - return []; - } else { + if (!this.asyncDataSource) { // In sync mode, a post-focus empty search or // click event displays the whole list. return this.entrylist; diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html index 38908aeed6..d7cff9f0d9 100644 --- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html +++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html @@ -69,7 +69,7 @@
@@ -86,6 +86,28 @@ +
+
+
+
+ + +
+
+ + +
+
+
+
+
+
+ + +
+
+
+
-- 2.43.2