LP#1708291: improvements to egEditFmRecord
authorGalen Charlton <gmc@equinoxinitiative.org>
Wed, 10 May 2017 21:36:44 +0000 (17:36 -0400)
committerDan Wells <dbw2@calvin.edu>
Fri, 1 Sep 2017 16:47:05 +0000 (12:47 -0400)
egEditFmRecord now knows how to specify that a custom Angular template
be used to supply the input widget for a given field; the initial use
of this will be allowing the prediction pattern template editor to be
used to set the pattern in a pattern template. The customFieldTemplates
attribute is used for this purpose.

This patch also teaches egEditFmRecord when to allow an org unit
selector to default to the workstation OU. The orgDefaultAllowed
attribute is used for this purpose.

Finally, a fixes a bug that ensures that the Save button is active
only when the entire form is valid.

Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Signed-off-by: Kathy Lussier <klussier@masslnc.org>
Signed-off-by: Dan Wells <dbw2@calvin.edu>
Open-ILS/src/templates/staff/share/t_fm_record_editor.tt2
Open-ILS/web/js/ui/default/staff/services/fm_record_editor.js

index f2328a1..b34fb4c 100644 (file)
@@ -1,4 +1,4 @@
-<form role="form" class="form-validated eg-edit-fm-record">
+<form role="form" class="form-validated eg-edit-fm-record" name="fm_record_form">
 
   <div class="modal-header">
     <button type="button" class="close"
         <label for="rec-{{field.name}}">{{field.label}}</label>
       </div>
       <div class="col-md-9">
-        <span  ng-if="field.datatype == 'id' && !id_is_editable">{{rec[field.name]()}}</span>
-        <input ng-if="field.datatype == 'id' &&  id_is_editable"
-          ng-readonly="field.readonly"
-          ng-required="field.is_required()"
-          ng-model="rec[field.name]"
-          ng-model-options="{ getterSetter : true }">
-        </input>
-        <input ng-if="field.datatype == 'text'"
-          ng-readonly="field.readonly"
-          ng-required="field.is_required()"
-          ng-model="rec[field.name]"
-          ng-model-options="{ getterSetter : true }">
-        </input>
-        <input ng-if="field.datatype == 'int'"
-          type="number"
-          ng-readonly="field.readonly"
-          ng-required="field.is_required()"
-          ng-model="rec[field.name]"
-          ng-model-options="{ getterSetter : true }">
-        </input>
-        <input ng-if="field.datatype == 'float'"
-          type="number" step="0.1"
-          ng-readonly="field.readonly"
-          ng-required="field.is_required()"
-          ng-model="rec[field.name]"
-          ng-model-options="{ getterSetter : true }">
-        </input>
-        <input ng-if="field.datatype == 'bool'"
-          type="checkbox"
-          ng-readonly="field.readonly"
-          ng-model="rec[field.name]"
-          ng-model-options="{ getterSetter : true }">
-        </input>
-        <span ng-if="field.datatype == 'link'"
-          ng-class="{nullable : !field.is_required()}">
-          <select ng-if="field.datatype == 'link'"
+        <span  ng-if="field.use_custom_template">
+          <eg-fm-custom-field-input template="field.custom_template" handlers="field.handlers">
+        </span>
+        <span  ng-if="!field.use_custom_template">
+          <span  ng-if="field.datatype == 'id' && !id_is_editable">{{rec[field.name]()}}</span>
+          <input ng-if="field.datatype == 'id' &&  id_is_editable"
+            ng-readonly="field.readonly"
+            ng-required="field.is_required()"
+            ng-model="rec[field.name]"
+            ng-model-options="{ getterSetter : true }">
+          </input>
+          <input ng-if="field.datatype == 'text'"
+            ng-readonly="field.readonly"
+            ng-required="field.is_required()"
+            ng-model="rec[field.name]"
+            ng-model-options="{ getterSetter : true }">
+          </input>
+          <input ng-if="field.datatype == 'int'"
+            type="number"
+            ng-readonly="field.readonly"
+            ng-required="field.is_required()"
+            ng-model="rec[field.name]"
+            ng-model-options="{ getterSetter : true }">
+          </input>
+          <input ng-if="field.datatype == 'float'"
+            type="number" step="0.1"
             ng-readonly="field.readonly"
             ng-required="field.is_required()"
-            ng-options="item.id as item.name for item in field.linked_values"
             ng-model="rec[field.name]"
             ng-model-options="{ getterSetter : true }">
-          </select>
+          </input>
+          <input ng-if="field.datatype == 'bool'"
+            type="checkbox"
+            ng-readonly="field.readonly"
+            ng-model="rec[field.name]"
+            ng-model-options="{ getterSetter : true }">
+          </input>
+          <span ng-if="field.datatype == 'link'"
+            ng-class="{nullable : !field.is_required()}">
+            <select ng-if="field.datatype == 'link'"
+              ng-readonly="field.readonly"
+              ng-required="field.is_required()"
+              ng-options="item.id as item.name for item in field.linked_values"
+              ng-model="rec[field.name]"
+              ng-model-options="{ getterSetter : true }">
+            </select>
+          </span>
+          <eg-org-selector ng-if="field.datatype == 'org_unit' && !field.org_default_allowed"
+            selected="rec_org_values[field.name]"
+            onchange="rec_orgs[field.name]" nodefault>
+          </eg-org-selector>
+          <eg-org-selector ng-if="field.datatype == 'org_unit' && field.org_default_allowed"
+            selected="rec_org_values[field.name]"
+            onchange="rec_orgs[field.name]">
+          </eg-org-selector>
         </span>
-        <eg-org-selector ng-if="field.datatype == 'org_unit'"
-          selected="rec_org_values[field.name]"
-          onchange="rec_orgs[field.name]" nodefault>
-        </eg-org-selector>
       </div>
     </div>
   </div>
   <div class="modal-footer">
-    <button class="btn btn-primary" ng-click="ok()">[% l('Save') %]</button>
+    <button class="btn btn-primary" type="submit" ng-disabled="fm_record_form.$invalid" ng-click="ok()">[% l('Save') %]</button>
     <button class="btn btn-warning" ng-click="cancel()">[% l('Cancel') %]</button>
   </div>
 </form>
index 8157f18..2e08c07 100644 (file)
@@ -16,6 +16,14 @@ angular.module('egFmRecordEditorMod',
             // record ID to update
             recordId : '=',
 
+            // fields with custom templates
+            // hash keyed on field name; may contain
+            //   template - Angular template; should access
+            //              field value using rec_flat[field.name]
+            //   handlers - any functions you want to pass
+            //              in to the custom template
+            customFieldTemplates : '=',
+
             // comma-separated list of fields that should not be
             // displayed
             hiddenFields : '@',
@@ -28,6 +36,10 @@ angular.module('egFmRecordEditorMod',
             // supplements what the IDL considers required
             requiredFields : '@',
 
+            // comma-separated list of org_unit fields where
+            // the selector should default to the workstation OU
+            orgDefaultAllowed : '@',
+
             // hash, keyed by field name, of functions to invoke
             // to check whether a field is required.  Each
             // callback is passed the field name and the record
@@ -71,9 +83,11 @@ angular.module('egFmRecordEditorMod',
             $scope.required = list_to_hash($scope.requiredFields);
             $scope.readonly = list_to_hash($scope.readonlyFields);
             $scope.hidden = list_to_hash($scope.hiddenFields);
+            $scope.org_default_allowed = list_to_hash($scope.orgDefaultAllowed);
 
             $scope.record_label = egCore.idl.classes[$scope.idlClass].label;
             $scope.rec_orgs = {};
+            $scope.rec_flat = {};
             $scope.rec_org_values = {};
             $scope.id_is_editable = false;
 
@@ -114,6 +128,12 @@ angular.module('egFmRecordEditorMod',
                             rec[field.name]('f');
                         }
                     }
+                    // 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]);
+                    }
                 });
             }
 
@@ -169,6 +189,13 @@ angular.module('egFmRecordEditorMod',
                         if ($scope.rec[field.name]()) {
                             $scope.rec_org_values[field.name] = $scope.rec_orgs[field.name]();
                         }
+                        field.org_default_allowed = (field.name in $scope.org_default_allowed);
+                    }
+                    if (field.name in $scope.customFieldTemplates) {
+                        field.use_custom_template = true;
+                        field.custom_template = $scope.customFieldTemplates[field.name].template;
+                        field.handlers = $scope.customFieldTemplates[field.name].handlers;
+                        $scope.rec_flat[field.name] = $scope.rec[field.name]();
                     }
                 });
                 return fields.filter(function(field) { return !(field.name in $scope.hidden) });
@@ -193,3 +220,17 @@ angular.module('egFmRecordEditorMod',
         }]
     };
 })
+
+.directive('egFmCustomFieldInput', function($compile) {
+    return {
+        restrict : 'E',
+        scope : {
+            template : '=',
+            handlers : '='
+        },
+        link : function(scope, element, attrs) {
+            element.html(scope.template);
+            $compile(element.contents())(scope.$parent);
+        }
+    };
+})