From b901a7e97d7e7be96aceb8cff3cbec6ac83b6cc6 Mon Sep 17 00:00:00 2001 From: Bill Erickson <berickxx@gmail.com> Date: Thu, 3 Jan 2019 10:17:42 -0500 Subject: [PATCH] LP1809288 Angular fm-editor read-only additions * Add read-only view to org-select * FM editor displays read-only values as plain text * Mark readOnly checkboxes "disabled" * Link fields only fetch linked data when an IDL selector exists * Minor code/style changes ** Define all class vars before class methods (ng-lint) ** Replace some tabs with spaces (ng-lint) ** Avoid unnecessary type defs with default values (ng-lint) ** More const goodness (ng-lint) ** camelCase some vars for consistentcy Signed-off-by: Bill Erickson <berickxx@gmail.com> Signed-off-by: Jane Sandberg <sandbej@linnbenton.edu> --- .../app/share/fm-editor/fm-editor.component.html | 180 ++++++++++++--------- .../src/app/share/fm-editor/fm-editor.component.ts | 46 ++++-- .../app/share/org-select/org-select.component.html | 32 ++-- .../app/share/org-select/org-select.component.ts | 9 +- .../app/staff/admin/basic-admin-page.component.ts | 8 +- 5 files changed, 169 insertions(+), 106 deletions(-) diff --git a/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.html b/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.html index 4aab72d7b4..233c3027d9 100644 --- a/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.html +++ b/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.html @@ -27,28 +27,42 @@ {{record[field.name]()}} </span> - <input *ngIf="field.datatype == 'id' && pkeyIsEditable" - class="form-control" - name="{{field.name}}" - id="{{idPrefix}}-{{field.name}}" - placeholder="{{field.label}}..." - i18n-placeholder - [readonly]="field.readOnly" - [required]="field.isRequired()" - [ngModel]="record[field.name]()" - (ngModelChange)="record[field.name]($event)"/> + <ng-container *ngIf="field.datatype == 'id' && pkeyIsEditable"> + <ng-container *ngIf="field.readOnly"> + <span>{{record[field.name]()}}</span> + </ng-container> + <ng-container *ngIf="!field.readOnly"> + <input + class="form-control" + name="{{field.name}}" + id="{{idPrefix}}-{{field.name}}" + placeholder="{{field.label}}..." + i18n-placeholder + [required]="field.isRequired()" + [ngModel]="record[field.name]()" + (ngModelChange)="record[field.name]($event)"/> + </ng-container> + </ng-container> - <input *ngIf="field.datatype == 'text' || field.datatype == 'interval'" - class="form-control" - name="{{field.name}}" - id="{{idPrefix}}-{{field.name}}" - placeholder="{{field.label}}..." - i18n-placeholder - [readonly]="field.readOnly" - [required]="field.isRequired()" - [ngModel]="record[field.name]()" - (ngModelChange)="record[field.name]($event)"/> + <ng-container + *ngIf="field.datatype == 'text' || field.datatype == 'interval'"> + <ng-container *ngIf="field.readOnly"> + <span>{{record[field.name]()}}</span> + </ng-container> + <ng-container *ngIf="!field.readOnly"> + <input + class="form-control" + name="{{field.name}}" + id="{{idPrefix}}-{{field.name}}" + placeholder="{{field.label}}..." + i18n-placeholder + [required]="field.isRequired()" + [ngModel]="record[field.name]()" + (ngModelChange)="record[field.name]($event)"/> + </ng-container> + </ng-container> + <!-- TODO: add support to eg-date-select for read-only view --> <span *ngIf="field.datatype == 'timestamp'"> <eg-date-select domId="{{idPrefix}}-{{field.name}}" @@ -57,83 +71,101 @@ </eg-date-select> </span> - <input *ngIf="field.datatype == 'int'" - class="form-control" - type="number" - name="{{field.name}}" - id="{{idPrefix}}-{{field.name}}" - placeholder="{{field.label}}..." - i18n-placeholder - [readonly]="field.readOnly" - [required]="field.isRequired()" - [ngModel]="record[field.name]()" - (ngModelChange)="record[field.name]($event)"/> - - <input *ngIf="field.datatype == 'float'" - class="form-control" - type="number" step="0.1" - name="{{field.name}}" - id="{{idPrefix}}-{{field.name}}" - placeholder="{{field.label}}..." - i18n-placeholder - [readonly]="field.readOnly" - [required]="field.isRequired()" - [ngModel]="record[field.name]()" - (ngModelChange)="record[field.name]($event)"/> - - <span *ngIf="field.datatype == 'money'"> - <!-- in read-only mode display the local-aware currency --> - <input *ngIf="field.readOnly" - class="form-control" - type="number" step="0.1" - name="{{field.name}}" - id="{{idPrefix}}-{{field.name}}" - [readonly]="field.readOnly" - [required]="field.isRequired()" - [ngModel]="record[field.name]() | currency"/> - - <input *ngIf="!field.readOnly" + <ng-container *ngIf="field.datatype == 'int'"> + <ng-container *ngIf="field.readOnly"> + <span>{{record[field.name]()}}</span> + </ng-container> + <ng-container *ngIf="!field.readOnly"> + + <input class="form-control" - type="number" step="0.1" + type="number" name="{{field.name}}" id="{{idPrefix}}-{{field.name}}" placeholder="{{field.label}}..." i18n-placeholder - [readonly]="field.readOnly" [required]="field.isRequired()" [ngModel]="record[field.name]()" (ngModelChange)="record[field.name]($event)"/> - </span> + </ng-container> + </ng-container> + + <ng-container *ngIf="field.datatype == 'float'"> + <ng-container *ngIf="field.readOnly"> + <span>{{record[field.name]()}}</span> + </ng-container> + <ng-container *ngIf="!field.readOnly"> + <input + class="form-control" + type="number" step="0.1" + name="{{field.name}}" + id="{{idPrefix}}-{{field.name}}" + placeholder="{{field.label}}..." + i18n-placeholder + [required]="field.isRequired()" + [ngModel]="record[field.name]()" + (ngModelChange)="record[field.name]($event)"/> + </ng-container> + </ng-container> + + <ng-container *ngIf="field.datatype == 'money'"> + <ng-container *ngIf="field.readOnly"> + <span>{{record[field.name]() | currency}}</span> + </ng-container> + <ng-container *ngIf="!field.readOnly"> + <input + class="form-control" + type="number" step="0.1" + name="{{field.name}}" + id="{{idPrefix}}-{{field.name}}" + placeholder="{{field.label}}..." + i18n-placeholder + [readonly]="field.readOnly" + [required]="field.isRequired()" + [ngModel]="record[field.name]()" + (ngModelChange)="record[field.name]($event)"/> + </ng-container> + </ng-container> <input *ngIf="field.datatype == 'bool'" class="form-check-input" type="checkbox" name="{{field.name}}" id="{{idPrefix}}-{{field.name}}" - [readonly]="field.readOnly" + [disabled]="field.readOnly" [ngModel]="record[field.name]()" (ngModelChange)="record[field.name]($event)"/> - <span *ngIf="field.datatype == 'link'" - [ngClass]="{nullable : !field.isRequired()}"> - <select - class="form-control" - name="{{field.name}}" - id="{{idPrefix}}-{{field.name}}" - [disabled]="field.readOnly" - [required]="field.isRequired()" - [ngModel]="record[field.name]()" - (ngModelChange)="record[field.name]($event)"> - <option *ngFor="let item of field.linkedValues" - [value]="item.id">{{item.name}}</option> - </select> - </span> + <ng-container *ngIf="field.datatype == 'link'"> + <ng-container *ngIf="field.readOnly"> + <!-- in readOnly mode, if a value is presetn, it will + live as the only item in the linkedValues array --> + <ng-container *ngIf="field.linkedValues[0]"> + <span>{{field.linkedValues[0].name}}</span> + </ng-container> + </ng-container> + <ng-container *ngIf="!field.readOnly"> + <span [ngClass]="{nullable : !field.isRequired()}"> + <select + class="form-control" + name="{{field.name}}" + id="{{idPrefix}}-{{field.name}}" + [required]="field.isRequired()" + [ngModel]="record[field.name]()" + (ngModelChange)="record[field.name]($event)"> + <option *ngFor="let item of field.linkedValues" + [value]="item.id">{{item.name}}</option> + </select> + </span> + </ng-container> + </ng-container> <eg-org-select *ngIf="field.datatype == 'org_unit'" placeholder="{{field.label}}..." i18n-placeholder domId="{{idPrefix}}-{{field.name}}" [limitPerms]="modePerms[mode]" + [readOnly]="field.readOnly" [applyDefault]="field.orgDefaultAllowed" [initialOrgId]="record[field.name]()" (onChange)="record[field.name]($event)"> diff --git a/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.ts b/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.ts index 7b90a02437..25d05525bd 100644 --- a/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.ts +++ b/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.ts @@ -100,6 +100,9 @@ export class FmRecordEditorComponent // list of fields on the IDL, since some are hidden, virtual, etc. fields: any[]; + // DOM id prefix to prevent id collisions. + idPrefix: string; + @Input() editMode(mode: 'create' | 'update' | 'view') { this.mode = mode; } @@ -110,8 +113,6 @@ export class FmRecordEditorComponent if (id) { this.recId = id; } } - idPrefix: string; - constructor( private modal: NgbModal, // required for passing to parent private idl: IdlService, @@ -127,8 +128,8 @@ export class FmRecordEditorComponent this.idlDef = this.idl.classes[this.idlClass]; this.recordLabel = this.idlDef.label; - // Add some randomness to the generated DOM IDs to ensure against clobbering - this.idPrefix = 'fm-editor-' + Math.floor(Math.random() * 100000); + // Add some randomness to the generated DOM IDs to ensure against clobbering + this.idPrefix = 'fm-editor-' + Math.floor(Math.random() * 100000); } // Opening dialog, fetch data. @@ -256,16 +257,33 @@ export class FmRecordEditorComponent }; } - if (field.datatype === 'link' && field.readOnly) { // no need to fetch all possible values for read-only fields - let id_to_fetch = this.record[field.name](); - if (id_to_fetch) { - promises.push( - this.pcrud.retrieve(field.class, this.record[field.name]()) - .toPromise().then(list => { - field.linkedValues = - this.flattenLinkedValues(field.class, Array(list)); - }) - ); + if (field.datatype === 'link' && field.readOnly) { + + // no need to fetch all possible values for read-only fields + const idToFetch = this.record[field.name](); + + if (idToFetch) { + + // If the linked class defines a selector field, fetch the + // linked data so we can display the data within the selector + // field. Otherwise, avoid the network lookup and let the + // bare value (usually an ID) be displayed. + const idField = this.idl.classes[field.class].pkey; + const selector = + this.idl.classes[field.class].field_map[idField].selector; + + if (selector && selector !== field.name) { + promises.push( + this.pcrud.retrieve(field.class, this.record[field.name]()) + .toPromise().then(list => { + field.linkedValues = + this.flattenLinkedValues(field.class, Array(list)); + }) + ); + } else { + // No selector, display the raw id/key value. + field.linkedValues = [{id: idToFetch, name: idToFetch}]; + } } } else if (field.datatype === 'link') { promises.push( diff --git a/Open-ILS/src/eg2/src/app/share/org-select/org-select.component.html b/Open-ILS/src/eg2/src/app/share/org-select/org-select.component.html index b03211420d..d4ffd53cc0 100644 --- a/Open-ILS/src/eg2/src/app/share/org-select/org-select.component.html +++ b/Open-ILS/src/eg2/src/app/share/org-select/org-select.component.html @@ -4,15 +4,23 @@ {{r.label}} </ng-template> -<input type="text" - class="form-control" - [attr.id]="domId.length ? domId : null" - [placeholder]="placeholder" - [(ngModel)]="selected" - [ngbTypeahead]="filter" - [resultTemplate]="displayTemplate" - [inputFormatter]="formatter" - (click)="click$.next($event.target.value)" - (selectItem)="orgChanged($event)" - #instance="ngbTypeahead" -/> +<ng-container *ngIf="readOnly"> + <span>{{selected.label}}</span> +</ng-container> + +<ng-container *ngIf="!readOnly"> + + <input type="text" + class="form-control" + [attr.id]="domId.length ? domId : null" + [placeholder]="placeholder" + [(ngModel)]="selected" + [ngbTypeahead]="filter" + [resultTemplate]="displayTemplate" + [inputFormatter]="formatter" + (click)="click$.next($event.target.value)" + (selectItem)="orgChanged($event)" + #instance="ngbTypeahead" + /> + +</ng-container> diff --git a/Open-ILS/src/eg2/src/app/share/org-select/org-select.component.ts b/Open-ILS/src/eg2/src/app/share/org-select/org-select.component.ts index 598a9886ae..25ed2b0b3a 100644 --- a/Open-ILS/src/eg2/src/app/share/org-select/org-select.component.ts +++ b/Open-ILS/src/eg2/src/app/share/org-select/org-select.component.ts @@ -54,6 +54,8 @@ export class OrgSelectComponent implements OnInit { // An onChange event WILL be generated when a default is applied. @Input() applyDefault = false; + @Input() readOnly = false; + // List of org unit IDs to exclude from the selector @Input() set hideOrgs(ids: number[]) { if (ids) { this.hidden = ids; } @@ -153,10 +155,13 @@ export class OrgSelectComponent implements OnInit { // Format for display in the selector drop-down and input. formatForDisplay(org: IdlObject): OrgDisplay { + let label = org[this.displayField](); + if (!this.readOnly) { + label = PAD_SPACE.repeat(org.ou_type().depth()) + label; + } return { id : org.id(), - label : PAD_SPACE.repeat(org.ou_type().depth()) - + org[this.displayField](), + label : label, disabled : false }; } diff --git a/Open-ILS/src/eg2/src/app/staff/admin/basic-admin-page.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/basic-admin-page.component.ts index 0427646abc..908dadf1da 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/basic-admin-page.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/basic-admin-page.component.ts @@ -19,7 +19,7 @@ export class BasicAdminPageComponent implements OnInit { idlClass: string; classLabel: string; persistKeyPfx: string; - readonlyFields: string = ''; + readonlyFields = ''; constructor( private route: ActivatedRoute, @@ -39,7 +39,7 @@ export class BasicAdminPageComponent implements OnInit { const data = this.route.snapshot.data[0]; if (data) { table = data.table; } } - const full_table = schema + '.' + table; + const fullTable = schema + '.' + table; // Set the prefix to "server", "local", "workstation", @@ -59,14 +59,14 @@ export class BasicAdminPageComponent implements OnInit { Object.keys(this.idl.classes).forEach(class_ => { const classDef = this.idl.classes[class_]; - if (classDef.table === full_table) { + if (classDef.table === fullTable) { this.idlClass = class_; this.classLabel = classDef.label; } }); if (!this.idlClass) { - throw new Error('Unable to find IDL class for table ' + full_table); + throw new Error('Unable to find IDL class for table ' + fullTable); } } } -- 2.11.0