From b5c5e1bbfa6724cb0eec28321274e33ec10f0073 Mon Sep 17 00:00:00 2001 From: Galen Charlton Date: Wed, 26 Feb 2020 10:39:10 -0500 Subject: [PATCH] LP#1850547: eg-combobox: add per-IDL-class formatting This patch teaches eg-combobox to apply display templates and label formatters for when idlClass is set. Currently templates are defined for acqf (to display the fund as "FUND_CODE (YEAR)" and acpl (to display the shelving location as "LABEL (OWNING_LIBRARY)". Signed-off-by: Galen Charlton Signed-off-by: Tiffany Little Signed-off-by: Bill Erickson --- .../share/combobox/combobox.component.html | 9 ++- .../app/share/combobox/combobox.component.ts | 64 +++++++++++++++++-- .../src/app/share/common-widgets.module.ts | 3 +- 3 files changed, 69 insertions(+), 7 deletions(-) diff --git a/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.html b/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.html index 83df22e20a..4f5ae6e66f 100644 --- a/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.html +++ b/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.html @@ -4,6 +4,13 @@ {{r.label || r.id}} + + {{r.fm.code()}} ({{r.fm.year()}}) + + + {{r.fm.name()}} ({{getOrgShortname(r.fm.owning_lib())}}) + +
*/ import {Component, OnInit, Input, Output, ViewChild, + Directive, ViewChildren, QueryList, AfterViewInit, TemplateRef, EventEmitter, ElementRef, forwardRef} from '@angular/core'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; 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 {IdlService} from '@eg/core/idl.service'; +import {IdlService, IdlObject} from '@eg/core/idl.service'; import {PcrudService} from '@eg/core/pcrud.service'; +import {OrgService} from '@eg/core/org.service'; export interface ComboboxEntry { id: any; @@ -19,6 +21,15 @@ export interface ComboboxEntry { label?: string; freetext?: boolean; userdata?: any; // opaque external value; ignored by this component. + fm?: IdlObject; +} + +@Directive({ + selector: 'ng-template[egIdlClass]' +}) +export class IdlClassTemplateDirective { + @Input() egIdlClass: string; + constructor(public template: TemplateRef) {} } @Component({ @@ -34,13 +45,15 @@ export interface ComboboxEntry { multi: true }] }) -export class ComboboxComponent implements ControlValueAccessor, OnInit { +export class ComboboxComponent implements ControlValueAccessor, OnInit, AfterViewInit { selected: ComboboxEntry; click$: Subject; entrylist: ComboboxEntry[]; @ViewChild('instance', { static: true }) instance: NgbTypeahead; + @ViewChild('defaultDisplayTemplate', { static: true}) t: TemplateRef; + @ViewChildren(IdlClassTemplateDirective) idlClassTemplates: QueryList; // Applies a name attribute to the input. // Useful in forms. @@ -95,7 +108,8 @@ export class ComboboxComponent implements ControlValueAccessor, OnInit { .subscribe(rec => { this.entrylist = [{ id: id, - label: rec[this.idlField]() + label: this.getFmRecordLabel(rec), + fm: rec }]; this.selected = this.entrylist.filter(e => e.id === id)[0]; }); @@ -157,6 +171,9 @@ export class ComboboxComponent implements ControlValueAccessor, OnInit { // and display. Default version trims leading/trailing spaces. formatDisplayString: (e: ComboboxEntry) => string; + idlDisplayTemplateMap: { [key: string]: TemplateRef } = {}; + getFmRecordLabel: (fm: IdlObject) => string; + // Stub functions required by ControlValueAccessor propagateChange = (_: any) => {}; propagateTouch = () => {}; @@ -166,6 +183,7 @@ export class ComboboxComponent implements ControlValueAccessor, OnInit { private store: StoreService, private idl: IdlService, private pcrud: PcrudService, + private org: OrgService, ) { this.entrylist = []; this.asyncIds = {}; @@ -177,6 +195,26 @@ export class ComboboxComponent implements ControlValueAccessor, OnInit { const display = result.label || result.id; return (display + '').trim(); }; + + this.getFmRecordLabel = (fm: IdlObject) => { + // FIXME: it would be cleaner if we could somehow use + // the per-IDL-class ng-templates directly + switch (this.idlClass) { + case 'acqf': + return fm.code() + ' (' + fm.year() + ')'; + break; + case 'acpl': + return fm.name() + ' (' + this.getOrgShortname(fm.owning_lib()) + ')'; + break; + default: + const field = this.idlField; + if (this.idlIncludeLibraryInLabel) { + return fm[field]() + ' (' + fm[this.idlIncludeLibraryInLabel]().shortname() + ')'; + } else { + return fm[field](); + } + } + }; } ngOnInit() { @@ -207,22 +245,38 @@ export class ComboboxComponent implements ControlValueAccessor, OnInit { return this.pcrud.search(this.idlClass, args, extra_args).pipe(map(data => { return { id: data[pkeyField](), - label: data[field]() + ' (' + data[this.idlIncludeLibraryInLabel]().shortname() + ')' + label: this.getFmRecordLabel(data), + fm: data }; })); } else { return this.pcrud.search(this.idlClass, args, extra_args).pipe(map(data => { - return {id: data[pkeyField](), label: data[field]()}; + return {id: data[pkeyField](), label: this.getFmRecordLabel(data), fm: data}; })); } }; } } + ngAfterViewInit() { + this.idlDisplayTemplateMap = this.idlClassTemplates.reduce((acc, cur) => { + acc[cur.egIdlClass] = cur.template; + return acc; + }, {}); + } + onClick($event) { this.click$.next($event.target.value); } + getOrgShortname(ou: any) { + if (typeof ou === 'object') { + return ou.shortname(); + } else { + return this.org.get(ou).shortname(); + } + } + openMe($event) { // Give the input a chance to focus then fire the click // handler to force open the typeahead diff --git a/Open-ILS/src/eg2/src/app/share/common-widgets.module.ts b/Open-ILS/src/eg2/src/app/share/common-widgets.module.ts index 5064f88964..c0b0f4e821 100644 --- a/Open-ILS/src/eg2/src/app/share/common-widgets.module.ts +++ b/Open-ILS/src/eg2/src/app/share/common-widgets.module.ts @@ -8,7 +8,7 @@ import {CommonModule} from '@angular/common'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {NgbModule} from '@ng-bootstrap/ng-bootstrap'; import {EgCoreModule} from '@eg/core/core.module'; -import {ComboboxComponent} from '@eg/share/combobox/combobox.component'; +import {ComboboxComponent, IdlClassTemplateDirective} from '@eg/share/combobox/combobox.component'; import {ComboboxEntryComponent} from '@eg/share/combobox/combobox-entry.component'; import {DateSelectComponent} from '@eg/share/date-select/date-select.component'; import {OrgSelectComponent} from '@eg/share/org-select/org-select.component'; @@ -27,6 +27,7 @@ import {FileReaderComponent} from '@eg/share/file-reader/file-reader.component'; DateRangeSelectComponent, DateTimeSelectComponent, FileReaderComponent, + IdlClassTemplateDirective ], imports: [ CommonModule, -- 2.43.2