observer: Observer<any>;
// The modalRef allows direct control of the modal instance.
- private modalRef: NgbModalRef = null;
+ protected modalRef: NgbModalRef = null;
constructor(private modalService: NgbModal) {}
</div>
<div class="row pt-1" *ngFor="let col of columnSet.columns"
[ngClass]="{visible : col.visible}">
- <div class="col-lg-1" (click)="col.visible=!col.visible">
+ <div class="col-lg-1" (click)="toggleVisibility(col)">
<span *ngIf="col.visible" class="badge badge-success">✓</span>
<span *ngIf="!col.visible" class="badge badge-warning">✗</span>
</div>
- <div class="col-lg-3" (click)="col.visible=!col.visible">{{col.label}}</div>
+ <div class="col-lg-3" (click)="toggleVisibility(col)">{{col.label}}</div>
<div class="col-lg-1">
<a class="no-href" title="Move column up" i18n-title
(click)="columnSet.moveColumn(col, -1)">
import {Component, Input, OnInit} from '@angular/core';
+import {Observable} from 'rxjs';
import {DialogComponent} from '@eg/share/dialog/dialog.component';
-import {GridColumnSet} from './grid';
+import {GridColumn, GridColumnSet, GridContext} from './grid';
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
@Component({
selector: 'eg-grid-column-config',
/**
*/
export class GridColumnConfigComponent extends DialogComponent implements OnInit {
- @Input() columnSet: GridColumnSet;
+ @Input() gridContext: GridContext;
+ columnSet: GridColumnSet;
+ changesPending = false;
+
+ open(ops: NgbModalOptions): Observable<any> {
+ this.changesPending = false;
+ this.columnSet = this.gridContext.columnSet;
+ return super.open(ops);
+ }
+
+ toggleVisibility(col: GridColumn) {
+ col.visible = !col.visible;
+ this.changesPending = true;
+ }
+
+ // Avoid reloading on each column change and instead reload the
+ // data if needed after all changes are complete.
+ // Override close() so we can reload data if needed.
+ // NOTE: ng-bootstrap v 8.0.0 has a 'closed' emitter, but
+ // we're not there yet.
+ close(value?: any) {
+ if (this.modalRef) { this.modalRef.close(); }
+ this.finalize();
+
+ if (this.changesPending && this.gridContext.reloadOnColumnChange) {
+ this.gridContext.reloadWithoutPagerReset();
+ }
+ }
}
@Input() disableTooltip: boolean;
@Input() asyncSupportsEmptyTermClick: boolean;
+ // Required columns are those that must be present in any auto-generated
+ // queries regardless of whether they are visible in the display.
+ @Input() required = false;
+
// get a reference to our container grid.
constructor(@Host() private grid: GridComponent) {}
col.path = this.path;
col.label = this.label;
col.flex = this.flex;
+ col.required = this.required;
col.hidden = this.hidden === true;
col.asyncSupportsEmptyTermClick = this.asyncSupportsEmptyTermClick === true;
col.isIndex = this.index === true;
--- /dev/null
+import {Injectable, EventEmitter, TemplateRef} from '@angular/core';
+import {Observable, empty, throwError} from 'rxjs';
+import {tap} from 'rxjs/operators';
+import {StoreService} from '@eg/core/store.service';
+import {LocaleService} from '@eg/core/locale.service';
+import {AuthService} from '@eg/core/auth.service';
+import {NetService} from '@eg/core/net.service';
+import {GridContext, GridColumnSort} from './grid';
+import {Pager} from '@eg/share/util/pager';
+
+interface FlatQueryFields {
+ [name: string]: string;
+}
+
+
+@Injectable()
+export class GridFlatDataService {
+
+ constructor(
+ private net: NetService,
+ private auth: AuthService
+ ) {}
+
+
+ getRows(gridContext: GridContext,
+ query: any, pager: Pager, sort: GridColumnSort[]): Observable<any> {
+
+ if (!gridContext.idlClass) {
+ return throwError('GridFlatDataService requires an idlClass');
+ }
+
+ const fields = this.compileFields(gridContext);
+ const flatSort = sort.map(s => {
+ const obj: any = {};
+ obj[s.name] = s.dir;
+ return obj;
+ });
+
+ return this.net.request(
+ 'open-ils.fielder',
+ 'open-ils.fielder.flattened_search',
+ this.auth.token(), gridContext.idlClass,
+ fields, query, {
+ sort: flatSort,
+ limit: pager.limit,
+ offset: pager.offset
+ }
+ );
+ }
+
+ compileFields(gridContext: GridContext): FlatQueryFields {
+ const fields: FlatQueryFields = {};
+
+ gridContext.columnSet.requiredColumns().forEach(col => {
+ // Verify the column describes a proper IDL field
+ const path = col.path || col.name;
+ const info = gridContext.columnSet.idlInfoFromDotpath(path);
+ if (info) { fields[col.name] = path; }
+ });
+
+ return fields;
+ }
+}
+
class="material-icons mat-icon-in-button">expand_less</span>
</button>
- <eg-grid-column-config #columnConfDialog [columnSet]="gridContext.columnSet">
+ <eg-grid-column-config #columnConfDialog [gridContext]="gridContext">
</eg-grid-column-config>
<div ngbDropdown placement="bottom-right">
<button ngbDropdownToggle class="btn btn-outline-dark no-dropdown-caret">
<div class="dropdown-divider"></div>
<a class="dropdown-item label-with-material-icon"
- (click)="col.visible=!col.visible"
+ (click)="toggleVisibility(col)"
*ngFor="let col of gridContext.columnSet.sortForColPicker()">
<span *ngIf="col.visible" class="badge badge-success">✓</span>
<span *ngIf="!col.visible" class="badge badge-warning">✗</span>
import {GridToolbarButton, GridToolbarAction, GridContext} from '@eg/share/grid/grid';
import {GridColumnWidthComponent} from './grid-column-width.component';
import {GridPrintComponent} from './grid-print.component';
+import {GridColumn} from './grid';
@Component({
selector: 'eg-grid-toolbar',
$event.preventDefault();
}
+
+ toggleVisibility(col: GridColumn) {
+ col.visible = !col.visible;
+ if (this.gridContext.reloadOnColumnChange) {
+ this.gridContext.reloadWithoutPagerReset();
+ }
+ }
}
// If set, appears along the top left side of the grid.
@Input() toolbarLabel: string;
+ // If true, showing/hiding columns will force the data source to
+ // refresh the current page of data.
+ @Input() reloadOnColumnChange = false;
+
context: GridContext;
// These events are emitted from our grid-body component.
this.context.disablePaging = this.disablePaging === true;
this.context.cellTextGenerator = this.cellTextGenerator;
this.context.ignoredFields = [];
+ this.context.reloadOnColumnChange = this.reloadOnColumnChange;
if (this.showFields) {
// Stripping spaces allows users to add newlines to
import {GridPrintComponent} from './grid-print.component';
import {GridFilterControlComponent} from './grid-filter-control.component';
import {GridToolbarActionsEditorComponent} from './grid-toolbar-actions-editor.component';
+import {GridFlatDataService} from './grid-flat-data.service';
@NgModule({
GridToolbarActionComponent
],
providers: [
+ GridFlatDataService
]
})
* Collection of grid related classses and interfaces.
*/
import {TemplateRef, EventEmitter, QueryList} from '@angular/core';
-import {Observable, Subscription} from 'rxjs';
+import {Observable, Subscription, empty} from 'rxjs';
import {IdlService, IdlObject} from '@eg/core/idl.service';
import {OrgService} from '@eg/core/org.service';
import {ServerStoreService} from '@eg/core/server-store.service';
disableTooltip: boolean;
asyncSupportsEmptyTermClick: boolean;
comparator: (valueA: any, valueB: any) => number;
+ required = false;
// True if the column was automatically generated.
isAuto: boolean;
return visible.concat(invisible);
}
+ requiredColumns(): GridColumn[] {
+ const visible = this.displayColumns();
+ return visible.concat(
+ this.columns.filter(c => c.required && !c.visible));
+ }
+
insertBefore(source: GridColumn, target: GridColumn) {
let targetIdx = -1;
let sourceIdx = -1;
disablePaging: boolean;
showDeclaredFieldsOnly: boolean;
cellTextGenerator: GridCellTextGenerator;
+ reloadOnColumnChange: boolean;
// Allow calling code to know when the select-all-rows-in-page
// action has occurred.
onChange: EventEmitter<boolean>;
}
+export interface GridColumnSort {
+ name: string;
+ dir: string;
+}
+
export class GridDataSource {
data: any[];
- sort: any[];
+ sort: GridColumnSort[];
filters: Object;
allRowsRetrieved: boolean;
requestingData: boolean;
retrievalError: boolean;
- getRows: (pager: Pager, sort: any[]) => Observable<any>;
+ getRows: (pager: Pager, sort: GridColumnSort[]) => Observable<any>;
constructor() {
this.sort = [];
}
}
-