Afform - convert afEntity and afField to components, standardize js filenames
authorColeman Watts <coleman@civicrm.org>
Thu, 14 Jan 2021 18:39:22 +0000 (13:39 -0500)
committerColeman Watts <coleman@civicrm.org>
Tue, 19 Jan 2021 17:23:28 +0000 (12:23 -0500)
18 files changed:
ext/afform/core/ang/af/Entity.js [deleted file]
ext/afform/core/ang/af/Field.js [deleted file]
ext/afform/core/ang/af/afEntity.component.js [new file with mode: 0644]
ext/afform/core/ang/af/afField.component.js [new file with mode: 0644]
ext/afform/core/ang/af/afField.html
ext/afform/core/ang/af/afFieldset.directive.js [moved from ext/afform/core/ang/af/Fieldset.js with 100% similarity]
ext/afform/core/ang/af/afJoin.directive.js [moved from ext/afform/core/ang/af/Join.js with 100% similarity]
ext/afform/core/ang/af/afRepeat.directive.js [moved from ext/afform/core/ang/af/Repeat.js with 96% similarity]
ext/afform/core/ang/af/afRepeat.html
ext/afform/core/ang/af/fields/ChainSelect.html
ext/afform/core/ang/af/fields/CheckBox.html
ext/afform/core/ang/af/fields/Date.html
ext/afform/core/ang/af/fields/Number.html
ext/afform/core/ang/af/fields/Radio.html
ext/afform/core/ang/af/fields/RichTextEditor.html
ext/afform/core/ang/af/fields/Select.html
ext/afform/core/ang/af/fields/Text.html
ext/afform/core/ang/af/fields/TextArea.html

diff --git a/ext/afform/core/ang/af/Entity.js b/ext/afform/core/ang/af/Entity.js
deleted file mode 100644 (file)
index 2c522a6..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-(function(angular, $, _) {
-  // Example usage: <af-form><af-entity name="Person" type="Contact" /> ... <fieldset af-fieldset="Person> ... </fieldset></af-form>
-  angular.module('af').directive('afEntity', function() {
-    // Whitelist of all allowed properties of an af-fieldset
-    // (at least the ones we care about client-side - other's can be added for server-side processing and we'll just ignore them)
-    var modelProps = {
-      type: '@',
-      data: '=',
-      modelName: '@name',
-      label: '@',
-      autofill: '@'
-    };
-    return {
-      restrict: 'E',
-      require: '^afForm',
-      scope: modelProps,
-      link: function($scope, $el, $attr, afFormCtrl) {
-        var ts = $scope.ts = CRM.ts('afform'),
-          entity = _.pick($scope, _.keys(modelProps));
-        entity.id = null;
-        afFormCtrl.registerEntity(entity);
-        // $scope.$watch('afEntity', function(newValue){$scope.myOptions = newValue;});
-      }
-    };
-  });
-})(angular, CRM.$, CRM._);
diff --git a/ext/afform/core/ang/af/Field.js b/ext/afform/core/ang/af/Field.js
deleted file mode 100644 (file)
index dcd925f..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-(function(angular, $, _) {
-  var id = 0;
-  // Example usage: <div af-fieldset="myModel"><af-field name="do_not_email" /></div>
-  angular.module('af').directive('afField', function(crmApi4) {
-    return {
-      restrict: 'E',
-      require: ['^^afForm', '^^afFieldset', '?^^afJoin', '?^^afRepeatItem'],
-      templateUrl: '~/af/afField.html',
-      scope: {
-        fieldName: '@name',
-        defn: '='
-      },
-      link: function($scope, $el, $attr, ctrls) {
-        var ts = $scope.ts = CRM.ts('afform'),
-          closestController = $($el).closest('[af-fieldset],[af-join],[af-repeat-item]'),
-          afForm = ctrls[0],
-          boolOptions = [{id: true, label: ts('Yes')}, {id: false, label: ts('No')}],
-          // Only used for is_primary radio button
-          noOptions = [{id: true, label: ''}];
-        $scope.dataProvider = closestController.is('[af-repeat-item]') ? ctrls[3] : ctrls[2] || ctrls[1];
-        $scope.fieldId = afForm.getFormMeta().name + '-' + $scope.fieldName + '-' + id++;
-
-        $el.addClass('af-field-type-' + _.kebabCase($scope.defn.input_type));
-
-        $scope.getOptions = function() {
-          return $scope.defn.options || ($scope.fieldName === 'is_primary' && $scope.defn.input_type === 'Radio' ? noOptions : boolOptions);
-        };
-
-        $scope.select2Options = function() {
-          return {
-            results: _.transform($scope.getOptions(), function(result, opt) {
-              result.push({id: opt.id, text: opt.label});
-            }, [])
-          };
-        };
-
-        // is_primary field - watch others in this afRepeat block to ensure only one is selected
-        if ($scope.fieldName === 'is_primary' && 'repeatIndex' in $scope.dataProvider) {
-          $scope.$watch('dataProvider.afRepeat.getEntityController().getData()', function (items, prev) {
-            var index = $scope.dataProvider.repeatIndex;
-            // Set first item to primary if there isn't a primary
-            if (items && !index && !_.find(items, 'is_primary')) {
-              $scope.dataProvider.getFieldData().is_primary = true;
-            }
-            // Set this item to not primary if another has been selected
-            if (items && prev && items.length === prev.length && items[index].is_primary && prev[index].is_primary &&
-              _.filter(items, 'is_primary').length > 1
-            ) {
-              $scope.dataProvider.getFieldData().is_primary = false;
-            }
-          }, true);
-        }
-
-        // ChainSelect - watch control field & reload options as needed
-        if ($scope.defn.input_type === 'ChainSelect') {
-          $scope.$watch('dataProvider.getFieldData()[defn.input_attrs.controlField]', function(val) {
-            if (val) {
-              var params = {
-                where: [['name', '=', $scope.fieldName]],
-                select: ['options'],
-                loadOptions: ['id', 'label'],
-                values: {}
-              };
-              params.values[$scope.defn.input_attrs.controlField] = val;
-              crmApi4($scope.dataProvider.getEntityType(), 'getFields', params, 0)
-                .then(function(data) {
-                  $scope.defn.options = data.options;
-                });
-            }
-          });
-        }
-      }
-    };
-  });
-})(angular, CRM.$, CRM._);
diff --git a/ext/afform/core/ang/af/afEntity.component.js b/ext/afform/core/ang/af/afEntity.component.js
new file mode 100644 (file)
index 0000000..6f0fef1
--- /dev/null
@@ -0,0 +1,25 @@
+(function(angular, $, _) {
+  // Whitelist of all allowed properties of an af-fieldset
+  // (at least the ones we care about client-side - other's can be added for server-side processing and we'll just ignore them)
+  var modelProps = {
+    type: '@',
+    data: '=',
+    modelName: '@name',
+    label: '@',
+    autofill: '@'
+  };
+  // Example usage: <af-form><af-entity name="Person" type="Contact" /> ... <fieldset af-fieldset="Person"> ... </fieldset></af-form>
+  angular.module('af').component('afEntity', {
+    require: {afForm: '^afForm'},
+    bindings: modelProps,
+    controller: function() {
+
+      this.$onInit = function() {
+        var entity = _.pick(this, _.keys(modelProps));
+        entity.id = null;
+        this.afForm.registerEntity(entity);
+      };
+    }
+
+  });
+})(angular, CRM.$, CRM._);
diff --git a/ext/afform/core/ang/af/afField.component.js b/ext/afform/core/ang/af/afField.component.js
new file mode 100644 (file)
index 0000000..3850382
--- /dev/null
@@ -0,0 +1,81 @@
+(function(angular, $, _) {
+  var id = 0;
+  // Example usage: <div af-fieldset="myModel"><af-field name="do_not_email" /></div>
+  angular.module('af').component('afField', {
+    require: {
+      afFieldset: '^^afFieldset',
+      afJoin: '?^^afJoin',
+      afRepeatItem: '?^^afRepeatItem'
+    },
+    templateUrl: '~/af/afField.html',
+    bindings: {
+      fieldName: '@name',
+      defn: '='
+    },
+    controller: function($scope, $element, crmApi4) {
+      var ts = $scope.ts = CRM.ts('afform'),
+        ctrl = this,
+        boolOptions = [{id: true, label: ts('Yes')}, {id: false, label: ts('No')}],
+        // Only used for is_primary radio button
+        noOptions = [{id: true, label: ''}];
+
+      this.$onInit = function() {
+        var closestController = $($element).closest('[af-fieldset],[af-join],[af-repeat-item]');
+        $scope.dataProvider = closestController.is('[af-repeat-item]') ? ctrl.afRepeatItem : ctrl.afJoin || ctrl.afFieldset;
+        $scope.fieldId = ctrl.fieldName + '-' + id++;
+
+        $element.addClass('af-field-type-' + _.kebabCase(ctrl.defn.input_type));
+
+        // is_primary field - watch others in this afRepeat block to ensure only one is selected
+        if (ctrl.fieldName === 'is_primary' && 'repeatIndex' in $scope.dataProvider) {
+          $scope.$watch('dataProvider.afRepeat.getEntityController().getData()', function (items, prev) {
+            var index = $scope.dataProvider.repeatIndex;
+            // Set first item to primary if there isn't a primary
+            if (items && !index && !_.find(items, 'is_primary')) {
+              $scope.dataProvider.getFieldData().is_primary = true;
+            }
+            // Set this item to not primary if another has been selected
+            if (items && prev && items.length === prev.length && items[index].is_primary && prev[index].is_primary &&
+              _.filter(items, 'is_primary').length > 1
+            ) {
+              $scope.dataProvider.getFieldData().is_primary = false;
+            }
+          }, true);
+        }
+
+        // ChainSelect - watch control field & reload options as needed
+        if (ctrl.defn.input_type === 'ChainSelect') {
+          $scope.$watch('dataProvider.getFieldData()[defn.input_attrs.controlField]', function(val) {
+            if (val) {
+              var params = {
+                where: [['name', '=', ctrl.fieldName]],
+                select: ['options'],
+                loadOptions: ['id', 'label'],
+                values: {}
+              };
+              params.values[ctrl.defn.input_attrs.controlField] = val;
+              crmApi4($scope.dataProvider.getEntityType(), 'getFields', params, 0)
+                .then(function(data) {
+                  ctrl.defn.options = data.options;
+                });
+            }
+          });
+        }
+
+      };
+
+      $scope.getOptions = function () {
+        return ctrl.defn.options || (ctrl.fieldName === 'is_primary' && ctrl.defn.input_type === 'Radio' ? noOptions : boolOptions);
+      };
+
+      $scope.select2Options = function() {
+        return {
+          results: _.transform($scope.getOptions(), function(result, opt) {
+            result.push({id: opt.id, text: opt.label});
+          }, [])
+        };
+      };
+
+    }
+  });
+})(angular, CRM.$, CRM._);
index 6fb2f55f859cc82a09e6700537da1bc18ae2292d..1d41d2cc13c1c21e593dc15d7233551d5ad3a597 100644 (file)
@@ -1,6 +1,6 @@
-<label class="crm-af-field-label" ng-if="defn.label" for="{{ fieldId }}">
-  {{ defn.label }}
+<label class="crm-af-field-label" ng-if="$ctrl.defn.label" for="{{:: fieldId }}">
+  {{:: $ctrl.defn.label }}
 </label>
-<p class="crm-af-field-help-pre" ng-if="defn.help_pre">{{ defn.help_pre }}</p>
-<div class="crm-af-field" ng-include="'~/af/fields/' + defn.input_type + '.html'"></div>
-<p class="crm-af-field-help-post" ng-if="defn.help_post">{{ defn.help_post }}</p>
+<p class="crm-af-field-help-pre" ng-if="$ctrl.defn.help_pre">{{:: $ctrl.defn.help_pre }}</p>
+<div class="crm-af-field" ng-include="'~/af/fields/' + $ctrl.defn.input_type + '.html'"></div>
+<p class="crm-af-field-help-post" ng-if="$ctrl.defn.help_post">{{:: $ctrl.defn.help_post }}</p>
similarity index 96%
rename from ext/afform/core/ang/af/Repeat.js
rename to ext/afform/core/ang/af/afRepeat.directive.js
index 2055f7edce47ba85dd3cd4c052ed1837775525a5..133897bf481d5db2815d07ce383c3a8726251847 100644 (file)
@@ -54,6 +54,7 @@
         }
       };
     })
+    // @internal directive used within the afRepeat directive, invoked once per iteration
     .directive('afRepeatItem', function() {
       return {
         restrict: 'A',
index 7c7ef36eb1ef4603ff864fa4d11892e1e4e3d41a..63cc10257ef7cf13d22484be343bdf9728fd7ca6 100644 (file)
@@ -1,5 +1,5 @@
 <div af-repeat-item="item" repeat-index="$index" ng-repeat="item in getItems()">
-  <ng-transclude />
+  <ng-transclude></ng-transclude>
   <button crm-icon="fa-ban" class="btn btn-xs af-repeat-remove-btn" ng-if="canRemove()" ng-click="removeItem($index)"></button>
 </div>
 <button crm-icon="{{ addIcon || 'fa-plus' }}" class="btn btn-sm af-repeat-add-btn" ng-if="canAdd()" ng-click="addItem()">{{ addLabel }}</button>
index a3cb1e2abd7b480b34c306ad7571167e147ef129..d34450e6507a33d16060c8b78cd02902b2cc6eb8 100644 (file)
@@ -1 +1 @@
-<input crm-ui-select="{data: select2Options, multiple: defn.input_attrs.multiple, placeholder: defn.input_attrs.placeholder}" id="{{ fieldId }}" ng-model="dataProvider.getFieldData()[fieldName]" />
+<input crm-ui-select="{data: select2Options, multiple: $ctrl.defn.input_attrs.multiple, placeholder: $ctrl.defn.input_attrs.placeholder}" id="{{:: fieldId }}" ng-model="dataProvider.getFieldData()[$ctrl.fieldName]" />
index 4bf900396f57f4ac93b81da3ce5ed3027b531ec7..8b1f8b21579f6312c610dfcfbbc4e8dee2754d4c 100644 (file)
@@ -1,7 +1,7 @@
-<ul class="crm-checkbox-list" id="{{ fieldId }}" ng-if="defn.options">
-  <li ng-repeat="opt in defn.options track by opt.id" >
-    <input type="checkbox" checklist-model="dataProvider.getFieldData()[fieldName]" id="{{ fieldId + opt.id }}" checklist-value="opt.id" />
+<ul class="crm-checkbox-list" id="{{ fieldId }}" ng-if="$ctrl.defn.options">
+  <li ng-repeat="opt in $ctrl.defn.options track by opt.id" >
+    <input type="checkbox" checklist-model="dataProvider.getFieldData()[$ctrl.fieldName]" id="{{ fieldId + opt.id }}" checklist-value="opt.id" />
     <label for="{{ fieldId + opt.id }}">{{ opt.label }}</label>
   </li>
 </ul>
-<input type="checkbox" ng-if="!defn.options" id="{{ fieldId }}" ng-model="dataProvider.getFieldData()[fieldName]" />
+<input type="checkbox" ng-if="!$ctrl.defn.options" id="{{ fieldId }}" ng-model="dataProvider.getFieldData()[$ctrl.fieldName]" />
index 5e7d7c21d7aa70dfd4e8ec78515b2fa8c11dfe5c..7e8a34f0fa450d9a617ef2505824311670bd2e3d 100644 (file)
@@ -1 +1 @@
-<input crm-ui-datepicker="defn.input_attrs" id="{{ fieldId }}" ng-model="dataProvider.getFieldData()[fieldName]" />
+<input crm-ui-datepicker="$ctrl.defn.input_attrs" id="{{ fieldId }}" ng-model="dataProvider.getFieldData()[$ctrl.fieldName]" />
index 111549c786546156d7c10ec05e84c4b92c7bba88..3b2b7bbc23dc8076d09c3402f43c051d8a478f05 100644 (file)
@@ -1 +1 @@
-<input class="crm-form-text" type="number" id="{{ fieldId }}" ng-model="dataProvider.getFieldData()[fieldName]" placeholder="{{ defn.input_attrs.placeholder }}" />
+<input class="crm-form-text" type="number" id="{{ fieldId }}" ng-model="dataProvider.getFieldData()[$ctrl.fieldName]" placeholder="{{ $ctrl.defn.input_attrs.placeholder }}" />
index 7bce6be407bd1d037af4b9df04835f94eb8bdc76..ea17957eb2fccc91096ca9d17909390b49054afa 100644 (file)
@@ -1,4 +1,4 @@
 <label ng-repeat="opt in getOptions() track by opt.id" >
-  <input class="crm-form-radio" type="radio" ng-model="dataProvider.getFieldData()[fieldName]" ng-value="opt.id" />
+  <input class="crm-form-radio" type="radio" ng-model="dataProvider.getFieldData()[$ctrl.fieldName]" ng-value="opt.id" />
   {{ opt.label }}
 </label>
index e2dbc6e9d1d3abdc687c87ea3c93568eb4e30ee5..4b5e52952a97359a44a2a294a1e8a62a6b31f3bb 100644 (file)
@@ -1 +1 @@
-<textarea crm-ui-richtext id="{{ fieldId }}" ng-model="dataProvider.getFieldData()[fieldName]" ></textarea>
+<textarea crm-ui-richtext id="{{ fieldId }}" ng-model="dataProvider.getFieldData()[$ctrl.fieldName]" ></textarea>
index a3cb1e2abd7b480b34c306ad7571167e147ef129..d34450e6507a33d16060c8b78cd02902b2cc6eb8 100644 (file)
@@ -1 +1 @@
-<input crm-ui-select="{data: select2Options, multiple: defn.input_attrs.multiple, placeholder: defn.input_attrs.placeholder}" id="{{ fieldId }}" ng-model="dataProvider.getFieldData()[fieldName]" />
+<input crm-ui-select="{data: select2Options, multiple: $ctrl.defn.input_attrs.multiple, placeholder: $ctrl.defn.input_attrs.placeholder}" id="{{:: fieldId }}" ng-model="dataProvider.getFieldData()[$ctrl.fieldName]" />
index 008b3441c693525ee859a1eede5d70db0dc73194..89cc8cefb262c42ebfd9b8df876b936077521366 100644 (file)
@@ -1 +1 @@
-<input class="crm-form-text" type="text" id="{{ fieldId }}" ng-model="dataProvider.getFieldData()[fieldName]" placeholder="{{ defn.input_attrs.placeholder }}" />
+<input class="crm-form-text" type="text" id="{{:: fieldId }}" ng-model="dataProvider.getFieldData()[$ctrl.fieldName]" placeholder="{{ $ctrl.defn.input_attrs.placeholder }}" />
index 66a2c5a37509f4b9bd0f73af6c0e34499a7e1a8b..6ded2d4f1ad5ab2764bca31dddf72aa6db21c186 100644 (file)
@@ -1 +1 @@
-<textarea class="crm-form-textarea" id="{{ fieldId }}" ng-model="dataProvider.getFieldData()[fieldName]" ></textarea>
+<textarea class="crm-form-textarea" id="{{:: fieldId }}" ng-model="dataProvider.getFieldData()[$ctrl.fieldName]" ></textarea>