From: Bill Erickson Date: Fri, 25 Jan 2019 20:17:56 +0000 (-0500) Subject: LP1812670 Angular grid shows selector labels X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=12f157512a973f65015fe1cf170093801a89bf85;p=working%2FEvergreen.git LP1812670 Angular grid shows selector labels * Teach PcrudService how to flesh link fields when a selector is defined on the linked class. This uses a new search/retrieve API flag {fleshSelectors:true}. * Teach the grid how to render selector values when configured to do so via a new grid component attribute [showLinkSelectors]="true". * Teach the Angular staff admin page to request linked selectors from pcrud and tell its grid to expect them. * Adds utility function to IdlServer for finding the selector for a given class + field. Signed-off-by: Bill Erickson --- diff --git a/Open-ILS/src/eg2/src/app/core/idl.service.ts b/Open-ILS/src/eg2/src/app/core/idl.service.ts index 89f8411de9..b6f8173159 100644 --- a/Open-ILS/src/eg2/src/app/core/idl.service.ts +++ b/Open-ILS/src/eg2/src/app/core/idl.service.ts @@ -133,5 +133,19 @@ export class IdlService { return result; } + + // Given a field on an IDL class, returns the name of the field + // on the linked class that acts as the selector for the linked class. + // Returns null if no selector is found or the field is not a link. + getLinkSelector(fmClass: string, field: string): string { + const fieldDef = this.classes[fmClass].field_map[field]; + if (fieldDef.class) { + const classDef = this.classes[fieldDef.class]; + if (classDef.pkey) { + return classDef.field_map[classDef.pkey].selector || null; + } + } + return null; + } } diff --git a/Open-ILS/src/eg2/src/app/core/pcrud.service.ts b/Open-ILS/src/eg2/src/app/core/pcrud.service.ts index 76ee341653..3c26fab71e 100644 --- a/Open-ILS/src/eg2/src/app/core/pcrud.service.ts +++ b/Open-ILS/src/eg2/src/app/core/pcrud.service.ts @@ -14,6 +14,10 @@ interface PcrudReqOps { anonymous?: boolean; idlist?: boolean; atomic?: boolean; + // If true, link-type fields which link to a class that defines a + // selector will be fleshed with the linked value. This affects + // retrieve(), retrieveAll(), and search() calls. + fleshSelectors?: boolean; } // For for documentation purposes. @@ -87,10 +91,42 @@ export class PcrudContext { this.session.disconnect(); } + // Adds "flesh" logic to retrieve linked values for all fields + // that link to a class which defines a selector field. + applySelectorFleshing(fmClass: string, pcrudOps: any) { + pcrudOps = pcrudOps || {}; + + if (!pcrudOps.flesh) { + pcrudOps.flesh = 1; + } + + if (!pcrudOps.flesh_fields) { + pcrudOps.flesh_fields = {}; + } + + this.idl.classes[fmClass].fields + .filter(f => f.datatype === 'link' && !f.virtual) + .forEach(field => { + const selector = this.idl.getLinkSelector(fmClass, field.name); + if (!selector) { return; } + + if (!pcrudOps.flesh_fields[fmClass]) { + pcrudOps.flesh_fields[fmClass] = []; + } + + if (pcrudOps.flesh_fields[fmClass].indexOf(field.name) < 0) { + pcrudOps.flesh_fields[fmClass].push(field.name); + } + }); + } + retrieve(fmClass: string, pkey: Number | string, pcrudOps?: any, reqOps?: PcrudReqOps): Observable { reqOps = reqOps || {}; this.authoritative = reqOps.authoritative || false; + if (reqOps.fleshSelectors) { + this.applySelectorFleshing(fmClass, pcrudOps); + } return this.dispatch( `open-ils.pcrud.retrieve.${fmClass}`, [this.token(reqOps), pkey, pcrudOps]); @@ -113,6 +149,10 @@ export class PcrudContext { if (reqOps.atomic) { method += '.atomic'; } + if (reqOps.fleshSelectors) { + this.applySelectorFleshing(fmClass, pcrudOps); + } + return this.dispatch(method, [this.token(reqOps), search, pcrudOps]); } diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts b/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts index 1fa4c2cfee..b9771e0c96 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts +++ b/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts @@ -78,6 +78,20 @@ export class GridComponent implements OnInit, AfterViewInit, OnDestroy { // grid data. @Input() pageOffset: number; + // If true and an idlClass is specificed, the grid assumes + // datatype=link fields that link to classes which define a selector + // are fleshed with the linked object. And, instead of displaying + // the raw field value, displays the selector value from the linked + // object. The caller is responsible for fleshing the appropriate + // fields in the GridDataSource getRows handler. + // + // This only applies to auto-generated columns. + // + // For example, idlClass="aou" and field="ou_type", the display + // value will be ou_type().name() since "name" is the selector + // field on the "aout" class. + @Input() showLinkSelectors: boolean; + context: GridContext; // These events are emitted from our grid-body component. @@ -109,6 +123,7 @@ export class GridComponent implements OnInit, AfterViewInit, OnDestroy { this.context.isSortable = this.sortable === true; this.context.isMultiSortable = this.multiSortable === true; this.context.useLocalSort = this.useLocalSort === true; + this.context.showLinkSelectors = this.showLinkSelectors === true; this.context.disableMultiSelect = this.disableMultiSelect === true; this.context.rowFlairIsEnabled = this.rowFlairIsEnabled === true; this.context.rowFlairCallback = this.rowFlairCallback; diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.ts b/Open-ILS/src/eg2/src/app/share/grid/grid.ts index 8a107038f3..b4ad72d207 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid.ts +++ b/Open-ILS/src/eg2/src/app/share/grid/grid.ts @@ -436,6 +436,7 @@ export class GridContext { defaultVisibleFields: string[]; defaultHiddenFields: string[]; overflowCells: boolean; + showLinkSelectors: boolean; // Services injected by our grid component idl: IdlService; @@ -623,12 +624,11 @@ export class GridContext { getRowColumnValue(row: any, col: GridColumn): string { let val; - if (col.name in row) { + + if (col.path) { + val = this.nestedItemFieldValue(row, col); + } else if (col.name in row) { val = this.getObjectFieldValue(row, col.name); - } else { - if (col.path) { - val = this.nestedItemFieldValue(row, col); - } } return this.format.transform({value: val, datatype: col.datatype}); } @@ -651,7 +651,7 @@ export class GridContext { for (let i = 0; i < steps.length; i++) { const step = steps[i]; - if (typeof obj !== 'object') { + if (obj === null || obj === undefined || typeof obj !== 'object') { // We have run out of data to step through before // reaching the end of the path. Conclude fleshing via // callback if provided then exit. @@ -855,6 +855,15 @@ export class GridContext { col.datatype = field.datatype; col.isIndex = (field.name === pkeyField); col.isAuto = true; + + if (this.showLinkSelectors) { + const selector = this.idl.getLinkSelector( + this.columnSet.idlClass, field.name); + if (selector) { + col.path = field.name + '.' + selector; + } + } + this.columnSet.add(col); }); } diff --git a/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.html b/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.html index 194f06b515..6878926763 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.html +++ b/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.html @@ -42,7 +42,7 @@ + [sortable]="true" persistKey="{{persistKey}}" [showLinkSelectors]="true"> diff --git a/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.ts b/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.ts index be4452b1b3..f92174a099 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/share/admin-page/admin-page.component.ts @@ -289,11 +289,13 @@ export class AdminPageComponent implements OnInit { const search = {}; search[this.orgField] = orgs; - return this.pcrud.search(this.idlClass, search, searchOps); + return this.pcrud.search( + this.idlClass, search, searchOps, {fleshSelectors: true}); } // No org filter -- fetch all rows - return this.pcrud.retrieveAll(this.idlClass, searchOps); + return this.pcrud.retrieveAll( + this.idlClass, searchOps, {fleshSelectors: true}); }; }