LP1858138 Link selector consolidation/repairs
authorBill Erickson <berickxx@gmail.com>
Thu, 2 Jan 2020 21:17:59 +0000 (16:17 -0500)
committerBill Erickson <berickxx@gmail.com>
Fri, 3 Jan 2020 19:28:24 +0000 (14:28 -0500)
Move more of the IDL link class selector extraction logic into the IDL
service.

Avoid using 'name' as a fall-through selector field when no 'name' field
exists on the class.

Teach the idl service to log warnings on invalid class and field name
combinations in selector lookups.

Modify fm-editor and combobox to use the new idl link selector
functions.

While we're in there, avoid unnecessary API calls from the combobox
by preventing async data lookups with a search term of "_CLICK_".

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Open-ILS/src/eg2/src/app/core/idl.service.ts
Open-ILS/src/eg2/src/app/share/combobox/combobox.component.ts
Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.ts

index b5abf61..fee3bd9 100644 (file)
@@ -140,6 +140,12 @@ export class IdlService {
     getLinkSelector(fmClass: string, field: string): string {
         let fieldDef = this.classes[fmClass].field_map[field];
 
+        if (!fieldDef) {
+            console.warn(
+                `No such field "${field}" for IDL class "${fmClass}"`);
+            return null;
+        }
+
         if (fieldDef.map) {
             // For mapped fields, we want the selector field on the
             // remotely linked object instead of the directly
@@ -149,11 +155,27 @@ export class IdlService {
         }
 
         if (fieldDef.class) {
-            const classDef = this.classes[fieldDef.class];
+            return this.getClassSelector(fieldDef.class);
+        }
+        return null;
+    }
+
+    // Return the selector field for the class.  If no selector is
+    // defined, use 'name' if it exists as a field on the class.
+    getClassSelector(idlClass: string): string {
+
+        if (idlClass) {
+            const classDef = this.classes[idlClass];
+
             if (classDef.pkey) {
-                return classDef.field_map[classDef.pkey].selector || null;
+                let selector = classDef.field_map[classDef.pkey].selector;
+                if (selector) { return selector; }
+
+                // No selector defined in the IDL, try 'name'.
+                if ('name' in classDef.field_map) { return 'name'; }
             }
         }
+
         return null;
     }
 
index ea49034..3d98604 100644 (file)
@@ -173,7 +173,7 @@ export class ComboboxComponent implements ControlValueAccessor, OnInit {
             }
 
             if (!this.idlField) {
-                this.idlField = classDef.field_map[classDef.pkey].selector || 'name';
+                this.idlField = this.idl.getClassSelector(this.idlClass);
             }
 
             this.asyncDataSource = term => {
@@ -332,8 +332,12 @@ export class ComboboxComponent implements ControlValueAccessor, OnInit {
 
         let searchTerm: string;
         searchTerm = term;
-        if (searchTerm === '_CLICK_' && this.asyncSupportsEmptyTermClick) {
-            searchTerm = '';
+        if (searchTerm === '_CLICK_') {
+            if (this.asyncSupportsEmptyTermClick) {
+                searchTerm = '';
+            } else {
+                return of();
+            }
         }
 
         return new Observable(observer => {
index 6b6d3eb..58c400f 100644 (file)
@@ -399,24 +399,13 @@ export class FmRecordEditorComponent
         });
     }
 
-    // Returns the name of the field on a class (typically via a linked
-    // field) that acts as the selector value for display / search.
-    getClassSelector(class_: string): string {
-        if (class_) {
-            const linkedClass = this.idl.classes[class_];
-            return linkedClass.pkey ?
-                linkedClass.field_map[linkedClass.pkey].selector : null;
-        }
-        return null;
-    }
-
     private flattenLinkedValues(field: any, list: IdlObject[]): ComboboxEntry[] {
         const class_ = field.class;
         const fieldOptions = this.fieldOptions[field.name] || {};
         const idField = this.idl.classes[class_].pkey;
 
         const selector = fieldOptions.linkedSearchField
-            || this.getClassSelector(class_) || idField;
+            || this.idl.getClassSelector(class_) || idField;
 
         return list.map(item => {
             return {id: item[idField](), label: item[selector]()};
@@ -499,7 +488,7 @@ export class FmRecordEditorComponent
                 // field.  Otherwise, avoid the network lookup and let the
                 // bare value (usually an ID) be displayed.
                 const selector = fieldOptions.linkedSearchField ||
-                    this.getClassSelector(field.class);
+                    this.idl.getClassSelector(field.class);
 
                 if (selector && selector !== field.name) {
                     promise = this.pcrud.retrieve(field.class, idToFetch)
@@ -544,7 +533,7 @@ export class FmRecordEditorComponent
         }
 
         const selector = fieldOptions.linkedSearchField ||
-            this.getClassSelector(field.class);
+            this.idl.getClassSelector(field.class);
 
         if (!selector && !fieldOptions.preloadLinkedValues) {
             // User probably expects an async data source, but we can't