From: Bill Erickson Date: Fri, 15 Jun 2018 15:51:58 +0000 (-0400) Subject: LP#1775466 Nested grid fields support X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=bd5abfbad7defedbc288383b9291f8a986c70ab5;p=working%2FEvergreen.git LP#1775466 Nested grid fields support Signed-off-by: Bill Erickson --- 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 5f9368263c..51565dc417 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 @@ -20,8 +20,8 @@ export class GridComponent implements OnInit, AfterViewInit, OnDestroy { @Input() mainLabel: string; @Input() dataSource: GridDataSource; @Input() idlClass: string; - @Input() isSortable: boolean; - @Input() isMultiSortable: boolean; + @Input() sortable: boolean; + @Input() multiSortable: boolean; @Input() persistKey: string; @Input() disableMultiSelect: boolean; @@ -46,8 +46,8 @@ export class GridComponent implements OnInit, AfterViewInit, OnDestroy { this.context.idlClass = this.idlClass; this.context.dataSource = this.dataSource; this.context.persistKey = this.persistKey; - this.context.isSortable = this.isSortable === true; - this.context.isMultiSortable = this.isMultiSortable === true; + this.context.isSortable = this.sortable === true; + this.context.isMultiSortable = this.multiSortable === true; this.context.disableMultiSelect = this.disableMultiSelect === true; this.context.init(); } 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 480ffe06e0..876c2e97eb 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid.ts +++ b/Open-ILS/src/eg2/src/app/share/grid/grid.ts @@ -30,6 +30,7 @@ export class GridColumn { isDragTarget: boolean; isSortable: boolean; isMultiSortable: boolean; + flesher: (obj: any, col: GridColumn, item: any) => any; getCellContext(row: any) { return { @@ -47,24 +48,22 @@ export class GridColumnSet { isSortable: boolean; isMultiSortable: boolean; stockVisible: string[]; + idl: IdlService; - constructor(idlClass?: string) { + constructor(idl: IdlService, idlClass?: string) { + this.idl = idl; this.columns = []; this.stockVisible = []; this.idlClass = idlClass; } add(col: GridColumn) { - // avoid dupes - if (this.getColByName(col.name)) { return; } - if (col.isIndex) { this.indexColumn = col; } - if (!col.flex) { col.flex = 2; } - if (!col.label) { col.label = col.name; } - if (!col.align) { col.align = 'left'; } - if (!col.datatype) { col.datatype = 'text'; } + this.applyColumnDefaults(col); - col.visible = !col.hidden; + if (this.getColByName(col.name)) { return; } // avoid dupes + + if (col.isIndex) { this.indexColumn = col; } // track which fields are visible on page load. if (col.visible) { @@ -80,6 +79,39 @@ export class GridColumnSet { return this.columns.filter(c => c.name === name)[0]; } + idlInfoFromDotpath(dotpath: string): any { + if (!dotpath) return null; + + let idlParent; + let idlField; + let idlClass = this.idl.classes[this.idlClass]; + + const pathParts = dotpath.split(/\./); + + for (let i = 0; i < pathParts.length; i++) { + const part = pathParts[i]; + idlParent = idlField; + idlField = idlClass.field_map[part]; + + if (idlField) { + if (idlField['class'] && ( + idlField.datatype === 'link' || + idlField.datatype === 'org_unit')) { + idlClass = this.idl.classes[idlField['class']]; + } + } else { + return null; + } + } + + return { + idlParent: idlParent, + idlField : idlField, + idlClass : idlClass + }; + } + + reset() { this.columns.forEach(col => { col.flex = 2; @@ -89,6 +121,28 @@ export class GridColumnSet { }); } + applyColumnDefaults(col: GridColumn) { + + if (!col.idlFieldDef && col.path) { + const idlInfo = this.idlInfoFromDotpath(col.path); + if (idlInfo) { + col.idlFieldDef = idlInfo.idlField; + if (!col.label) { + col.label = col.idlFieldDef.label || col.idlFieldDef.name; + col.datatype = col.idlFieldDef.datatype; + } + } + } + + if (!col.name) { col.name = col.path; } + if (!col.flex) { col.flex = 2; } + if (!col.align) { col.align = 'left'; } + if (!col.label) { col.label = col.name; } + if (!col.datatype) { col.datatype = 'text'; } + + col.visible = !col.hidden; + } + applyColumnSortability(col: GridColumn) { // column sortability defaults to the sortability of the column set. if (col.isSortable === undefined && this.isSortable) { @@ -297,7 +351,7 @@ export class GridContext { } init() { - this.columnSet = new GridColumnSet(this.idlClass); + this.columnSet = new GridColumnSet(this.idl, this.idlClass); this.columnSet.isSortable = this.isSortable === true; this.columnSet.isMultiSortable = this.isMultiSortable === true; this.generateColumns(); @@ -313,8 +367,8 @@ export class GridContext { destroy() { this.ignorePager(); - } + reload() { // Give the UI time to settle before reloading grid data. // This can help when data retrieval depends on a value @@ -381,14 +435,68 @@ export class GridContext { getRowColumnValue(row: any, col: GridColumn): string { let val; - if (typeof row[col.name] === 'function') { - val = row[col.name](); + if (col.name in row) { + val = this.getObjectFieldValue(row, col.name); } else { - val = row[col.name]; + if (col.path) { + val = this.nestedItemFieldValue(row, col); + } } return this.format.transform({value: val, datatype: col.datatype}); } + getObjectFieldValue(obj: any, name: string): any { + if (typeof obj[name] === 'function') { + return obj[name](); + } else { + return obj[name]; + } + } + + nestedItemFieldValue(obj: any, col: GridColumn): string { + + let idlField; + let idlClassDef; + const original = obj; + const steps = col.path.split('.'); + + for (let i = 0; i < steps.length; i++) { + const step = steps[i]; + + if (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. + if (col.flesher && obj !== undefined) { + return col.flesher(obj, col, original); + } + return obj; + } + + const class_ = obj.classname; + if (class_ && (idlClassDef = this.idl.classes[class_])) { + idlField = idlClassDef.field_map[step]; + } + + obj = this.getObjectFieldValue(obj, step); + } + + // We found a nested IDL object which may or may not have + // been configured as a top-level column. Flesh the column + // metadata with our newly found IDL info. + if (idlField) { + if (!col.datatype) { + col.datatype = idlField.datatype; + } + if (!col.label) { + col.label = idlField.label || idlField.name; + } + } + + return obj; + } + + getColumnTextContent(row: any, col: GridColumn): string { if (col.cellTemplate) { // TODO 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 fda77a4d0b..b96a7ce504 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 @@ -103,9 +103,13 @@ HELLO {{userContext.hello}} - - + + + +

+ diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts index 1dc600ff3e..46f2bd57a7 100644 --- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts @@ -9,6 +9,7 @@ import {take} from 'rxjs/operators/take'; import {GridDataSource} from '@eg/share/grid/grid'; import {IdlService, IdlObject} from '@eg/core/idl.service'; import {PcrudService} from '@eg/core/pcrud.service'; +import {OrgService} from '@eg/core/org.service'; import {Pager} from '@eg/share/util/pager'; import {DateSelectComponent} from '@eg/share/date-select/date-select.component'; import {PrintService} from '@eg/share/print/print.service'; @@ -52,6 +53,7 @@ export class SandboxComponent implements OnInit { constructor( private idl: IdlService, + private org: OrgService, private pcrud: PcrudService, private strings: StringService, private toast: ToastService, @@ -66,12 +68,22 @@ export class SandboxComponent implements OnInit { {name: 'The Tick', state: 'TX'} ]; - this.btSource.getRows = (pager: Pager) => { + this.btSource.getRows = (pager: Pager, sort: any[]) => { + + const orderBy: any = {cbt: 'name'}; + if (sort.length) { + orderBy.cbt = sort[0].name + ' ' + sort[0].dir; + } + return this.pcrud.retrieveAll('cbt', { offset: pager.offset, limit: pager.limit, - order_by: {cbt: 'name'} - }); + order_by: orderBy + }).pipe(map(cbt => { + // example of inline fleshing + cbt.owner(this.org.get(cbt.owner())); + return cbt; + })); }; /* 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 9c9c7ed639..600db00326 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 @@ -38,7 +38,7 @@ + [sortable]="true" persistKey="{{persistKey}}">