LP#1626157 fm editor wip; pcrud
authorBill Erickson <berickxx@gmail.com>
Wed, 18 Apr 2018 16:15:53 +0000 (12:15 -0400)
committerBill Erickson <berickxx@gmail.com>
Wed, 18 Apr 2018 16:15:53 +0000 (12:15 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/core/idl.service.ts
Open-ILS/src/eg2/src/app/core/pcrud.service.ts
Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.html
Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.ts

index fdbd111..f105140 100644 (file)
@@ -76,6 +76,45 @@ export class EgIdlService {
 
         for (var cls in this.classes) 
             mkclass(cls, this.classes[cls].fields);
-    };
+    }
+
+    // Makes a deep copy of an EgIdlObject's / structures containing
+    // EgIdlObject's.  Note we don't use JSON cross-walk because our
+    // JSON lib does not handle circular references.
+    // @depth specifies the maximum number of steps through EgIdlObject'
+    // we will traverse.
+    clone(source: any, depth?: number): any {
+        if (depth === undefined) depth = 100;
+
+        var result;
+        if (typeof source == 'undefined' || source === null) {
+            return source;
+
+        } else if (source._isfieldmapper) {
+            // same depth because we're still cloning this same object
+            result = this.create(source.classname, this.clone(source.a, depth));
+
+        } else {
+            if(Array.isArray(source)) {
+                result = [];
+            } else if(typeof source === 'object') { // source is not null
+                result = {};
+            } else {
+                return source; // primitive
+            }
+
+            for (var j in source) {
+                if (source[j] === null || typeof source[j] == 'undefined') {
+                    result[j] = source[j];
+                } else if(source[j]._isfieldmapper) {
+                    if (depth) result[j] = this.clone(source[j], depth - 1);
+                } else {
+                    result[j] = this.clone(source[j], depth);
+                }
+            }
+        }
+
+        return result;
+    }
 }
 
index 614f0ca..dddf209 100644 (file)
@@ -114,16 +114,16 @@ export class EgPcrudContext {
         return this.dispatch(method, [this.token(reqOps), search, pcrudOps]);
     }
 
-    create(list: EgIdlObject[]): Observable<EgPcrudResponse> {
+    create(list: EgIdlObject | EgIdlObject[]): Observable<EgPcrudResponse> {
         return this.cud('create', list)
     }
-    update(list: EgIdlObject[]): Observable<EgPcrudResponse> {
+    update(list: EgIdlObject | EgIdlObject[]): Observable<EgPcrudResponse> {
         return this.cud('update', list)
     }
-    remove(list: EgIdlObject[]): Observable<EgPcrudResponse> {
+    remove(list: EgIdlObject | EgIdlObject[]): Observable<EgPcrudResponse> {
         return this.cud('delete', list)
     }
-    autoApply(list: EgIdlObject[]): Observable<EgPcrudResponse> { // RENAMED
+    autoApply(list: EgIdlObject | EgIdlObject[]): Observable<EgPcrudResponse> { // RENAMED
         return this.cud('auto',   list)
     }
 
@@ -197,6 +197,7 @@ export class EgPcrudContext {
 
     private cud(action: string, 
         list: EgIdlObject | EgIdlObject[]): Observable<EgPcrudResponse> {
+        this.cudList = [].concat(list); // value or array
 
         this.log(`CUD(): ${action}`);
 
@@ -204,8 +205,6 @@ export class EgPcrudContext {
         this.cudAction = action;
         this.xactCloseMode = 'commit';
 
-        if (!Array.isArray(list)) this.cudList = [list];
-
         return this.wrapXact(() => {
             return Observable.create(observer => {
                 this.cudObserver = observer;
@@ -284,19 +283,19 @@ export class EgPcrudService {
         return this.newContext().search(fmClass, search, pcrudOps, reqOps);
     }
 
-    create(list: EgIdlObject[]): Observable<EgPcrudResponse> {
+    create(list: EgIdlObject | EgIdlObject[]): Observable<EgPcrudResponse> {
         return this.newContext().create(list);
     }
 
-    update(list: EgIdlObject[]): Observable<EgPcrudResponse> {
+    update(list: EgIdlObject | EgIdlObject[]): Observable<EgPcrudResponse> {
         return this.newContext().update(list);
     }
 
-    remove(list: EgIdlObject[]): Observable<EgPcrudResponse> {
+    remove(list: EgIdlObject | EgIdlObject[]): Observable<EgPcrudResponse> {
         return this.newContext().remove(list);
     }
 
-    autoApply(list: EgIdlObject[]): Observable<EgPcrudResponse> { 
+    autoApply(list: EgIdlObject | EgIdlObject[]): Observable<EgPcrudResponse> { 
         return this.newContext().autoApply(list);
     }
 }
index 7ded990..c3cd937 100644 (file)
@@ -1,6 +1,6 @@
 <ng-template #dialogContent>
   <div class="modal-header bg-info">
-    <h4 class="modal-title" i18n>Record Editor</h4>
+    <h4 class="modal-title" i18n>Record Editor: {{recordLabel}}</h4>
     <button type="button" class="close" 
       i18n-aria-label aria-label="Close" 
       (click)="dismiss('cross_click')">
     </form>
   </div>
   <div class="modal-footer">
-    <button type="button" class="btn btn-success" 
+    <button type="button" class="btn btn-success" *ngIf="mode == 'view'"
       (click)="close()" i18n>Close</button>
+    <button type="button" class="btn btn-warning" *ngIf="mode != 'view'"
+      (click)="cancel()" i18n>Cancel</button>
+    <button type="button" class="btn btn-success pl-1" *ngIf="mode != 'view'"
+      (click)="save()" i18n>Save</button>
   </div>
 </ng-template>
index 8e03bdf..cb4b587 100644 (file)
@@ -137,6 +137,36 @@ export class FmRecordEditorComponent
         });
     }
 
+    // Modifies the provided FM record in place, replacing JS values
+    // with IDL-compatible values.
+    convertDatatypesToIdl(rec: EgIdlObject) {
+        var fields = this.idlDef.fields;
+        fields.forEach(field => {
+            if (field.datatype == 'bool') {
+                if (rec[field.name]() == true) {
+                    rec[field.name]('t');
+                } else if (rec[field.name]() == false) {
+                    rec[field.name]('f');
+                }
+            } else if (field.datatype == 'org_unit') {
+                let org = rec[field.name]();
+                if (org != null && typeof org == 'object') {
+                    rec[field.name](org.id());
+                }
+            }
+
+            /* TODO
+            // retrieve values from any fields controlled
+            // by custom templates, which for the moment all
+            // expect to be passed an ordinary flat value
+            if (field.name in $scope.rec_flat) {
+                rec[field.name]($scope.rec_flat[field.name]);
+            }
+            */
+        });
+    }
+
+
     private flattenLinkedValues(cls: string, list: EgIdlObject[]): any[] {
         let idField = this.idl.classes[cls].pkey;
         let selector = 
@@ -193,12 +223,16 @@ export class FmRecordEditorComponent
     }
 
     save() {
-        // save changes here
-        this.close(/* data for caller */);
+        let recToSave = this.idl.clone(this.record);
+        this.convertDatatypesToIdl(recToSave);
+        this.pcrud[this.mode]([recToSave]).toPromise().then(
+            result => this.close(result),
+            error  => this.dismiss(error)
+        );
     }
 
     cancel() {
-        this.dismiss(/* data for caller */);
+        this.dismiss('canceled');
     }
 }