Add field widgets
authorColeman Watts <coleman@civicrm.org>
Wed, 26 Jun 2019 01:35:39 +0000 (21:35 -0400)
committerCiviCRM <info@civicrm.org>
Wed, 16 Sep 2020 02:13:18 +0000 (19:13 -0700)
Add field widgets

18 files changed:
ext/afform/core/CRM/Afform/Page/AfformBase.php
ext/afform/core/ang/af/Model.js
ext/afform/core/ang/af/ModelList.js
ext/afform/core/ang/af/ModelProp.js
ext/afform/core/ang/afField/afField.html
ext/afform/core/ang/afField/afField.js
ext/afform/core/ang/afField/widgets/CheckBox.html [new file with mode: 0644]
ext/afform/core/ang/afField/widgets/Date.html [new file with mode: 0644]
ext/afform/core/ang/afField/widgets/Number.html [new file with mode: 0644]
ext/afform/core/ang/afField/widgets/Radio.html [new file with mode: 0644]
ext/afform/core/ang/afField/widgets/RichTextEditor.html [new file with mode: 0644]
ext/afform/core/ang/afField/widgets/Select.html [new file with mode: 0644]
ext/afform/core/ang/afField/widgets/Text.html [new file with mode: 0644]
ext/afform/core/ang/afField/widgets/TextArea.html [new file with mode: 0644]
ext/afform/core/ang/afformCore.ang.php
ext/afform/core/ang/afformCore.js
ext/afform/core/ang/afformStandalone.ang.php
ext/afform/core/templates/CRM/Afform/Page/AfformBase.tpl

index 75daf17cba74ac30df0156e8d72f32edd2fa4543..ee666dacac1128823d5ea6d98b2090c4a41eef2f 100644 (file)
@@ -12,7 +12,9 @@ class CRM_Afform_Page_AfformBase extends CRM_Core_Page {
     $loader = new \Civi\Angular\AngularLoader();
     $loader->setModules([$module, 'afformStandalone']);
     $loader->setPageName(implode('/', $pagePath));
-    $loader->useApp();
+    $loader->useApp([
+      'file' => 'CRM/Afform/Page/AfformBase.tpl',
+    ]);
     $loader->getRes()->addSetting([
       'afform' => [
         'open' => _afform_angular_module_name($pageArgs['afform'], 'dash'),
index 8a07e9f5d3d7f1c284c4398f3d6b4e294e6162e1..3201e1af6788ff80e5888744ea0543ad150b40ca 100644 (file)
       },
       link: function($scope, $el, $attr, afModelListCtrl) {
         $scope.afModelListCtrl = afModelListCtrl;
-        // $scope.$watch('afName', function(newValue){
-        //   // $scope.myOptions = newValue;
-        //   $scope.modelDefn = afModelListCtrl.getEntity(newValue);
-        //   console.log('Lookup entity', newValue, $scope.modelDefn);
-        // });
+        // This is faster than waiting for each field directive to register itself
+        $('af-field', $el).each(function() {
+          afModelListCtrl.registerField($scope.afName, $(this).attr('field-name'))
+        });
       },
       controller: function($scope){
         this.getDefn = function getDefn() {
           return $scope.afModelListCtrl.getEntity($scope.afName);
           // return $scope.modelDefn;
+        };
+        this.getData = function getData() {
+          return $scope.afModelListCtrl.getData($scope.afName);
+        };
+        this.getName = function() {
+          return $scope.afName;
         }
       }
     };
index 1c6fc6f2077e7e3a7cd63b701cd33279ffb085bc..cdbe8b9f47ca9ce6f8ce989aeda809ce7ab2dd3d 100644 (file)
       },
       link: function($scope, $el, $attr) {},
       controller: ['$scope', function($scope) {
-        var entities = {};
+        var schema = {}, data = {};
 
         $scope.$parent[$scope.ctrl] = this;
+        // Maybe there's a better way to export this controller to scope?
+        $scope.myCtrl = this;
 
         this.registerEntity = function registerEntity(entity) {
-          // console.log('register', entity.name);
-          entities[entity.name] = entity;
+          schema[entity.name] = entity;
+          data[entity.name] = data[entity.name] || {};
+        };
+        this.registerField = function(entityName, fieldName) {
+          schema[entityName].fields.push(fieldName);
         };
         this.getEntity = function getEntity(name) {
-          // console.log('get', name);
-          return entities[name];
+          return schema[name];
+        };
+        this.getData = function getData(name) {
+          return data[name];
+        };
+        this.getSchema = function getSchema(name) {
+          return schema[name];
         };
-
-        // TODO: Support for tapping into load+save API calls
 
         this.submit = function submit() {
           CRM.alert('TODO: Submit');
         };
-      }]
+      }
     };
   });
 })(angular, CRM.$, CRM._);
index ef44b79feb7361aa37a0573033ed06ae8ec81283..399c6a60bf0bf81938ffe0556e6973ab43312326 100644 (file)
       link: function($scope, $el, $attr, afModelListCtrl) {
         var ts = $scope.ts = CRM.ts('afform');
         afModelListCtrl.registerEntity({
+          id: null,
           type: $scope.afType,
           name: $scope.afName,
-          label: $scope.afLabel
+          label: $scope.afLabel,
+          fields: [],
         });
         // $scope.$watch('afModelProp', function(newValue){$scope.myOptions = newValue;});
       }
index 3590517913a445d64c34322ecb4dd9e8e4d3eb51..f54a4bfede97b6d24f8c6768a2afe7f4589190ef 100644 (file)
@@ -1,3 +1,4 @@
-<div>
-  {{ts('Single field for %1::%2', {1: afModel.getDefn().type,2: fieldName})}}
-</div>
+<label class="crm-af-field-label" ng-if="fieldDefn.title" for="{{ fieldId }}">
+  {{ fieldDefn.title }}
+</label>
+<div class="crm-af-field" ng-include="'~/afField/widgets/' + fieldDefn.input_type + '.html'"></div>
index 911e8deed29c6e415f08a2990a86916a2f9d66cb..5d4fb7a1fae5268492fd359f9f6fd897bf0a66db 100644 (file)
@@ -2,15 +2,27 @@
   // Example usage: <af-model af-name="myModel"><af-field field-name="do_not_email" /></af-model>
   angular.module('afField').directive('afField', function() {
     return {
-      restrict: 'AE',
-      require: ['^afModel'],
+      restrict: 'E',
+      require: ['^afModel', '^afModelList'],
       templateUrl: '~/afField/afField.html',
       scope: {
-        fieldName: '@'
+        fieldName: '@',
+        fieldDefn: '='
       },
       link: function($scope, $el, $attr, ctrls) {
         var ts = $scope.ts = CRM.ts('afform');
         $scope.afModel = ctrls[0];
+        var modelList = ctrls[1];
+        $scope.fieldId = $scope.afModel.getDefn().name + '-' + $scope.fieldName;
+        $scope.getData = $scope.afModel.getData;
+
+        $scope.getOptions = function() {
+          return _.transform($scope.fieldDefn.options, function(result, val, key) {
+            result.push({id: key, text: val});
+          }, []);
+        };
+
+        $el.addClass('af-field-type-' + _.kebabCase($scope.fieldDefn.input_type));
       }
     };
   });
diff --git a/ext/afform/core/ang/afField/widgets/CheckBox.html b/ext/afform/core/ang/afField/widgets/CheckBox.html
new file mode 100644 (file)
index 0000000..d4401cc
--- /dev/null
@@ -0,0 +1,7 @@
+<ul class="crm-checkbox-list" id="{{ fieldId }}" ng-if="fieldDefn.options">
+  <li ng-repeat="(key, val) in fieldDefn.options" >
+    <input type="checkbox" checklist-model="getData()[fieldName]" id="{{ fieldId + key }}" checklist-value="key" />
+    <label for="{{ fieldId + key }}">{{ val }}</label>
+  </li>
+</ul>
+<input type="checkbox" ng-if="!fieldDefn.options" id="{{ fieldId }}" ng-model="getData()[fieldName]" />
diff --git a/ext/afform/core/ang/afField/widgets/Date.html b/ext/afform/core/ang/afField/widgets/Date.html
new file mode 100644 (file)
index 0000000..21d59e7
--- /dev/null
@@ -0,0 +1 @@
+<input crm-ui-datepicker="fieldDefn.input_attrs" id="{{ fieldId }}" ng-model="getData()[fieldName]" />
diff --git a/ext/afform/core/ang/afField/widgets/Number.html b/ext/afform/core/ang/afField/widgets/Number.html
new file mode 100644 (file)
index 0000000..d04da06
--- /dev/null
@@ -0,0 +1 @@
+<input class="crm-form-text" type="number" id="{{ fieldId }}" ng-model="getData()[fieldName]" />
diff --git a/ext/afform/core/ang/afField/widgets/Radio.html b/ext/afform/core/ang/afField/widgets/Radio.html
new file mode 100644 (file)
index 0000000..1149785
--- /dev/null
@@ -0,0 +1,4 @@
+<label ng-repeat="(key, val) in fieldDefn.options" >
+  <input class="crm-form-radio" type="radio" ng-model="getData()[fieldName]" value="{{ key }}" />
+  {{ val }}
+</label>
diff --git a/ext/afform/core/ang/afField/widgets/RichTextEditor.html b/ext/afform/core/ang/afField/widgets/RichTextEditor.html
new file mode 100644 (file)
index 0000000..cedb722
--- /dev/null
@@ -0,0 +1 @@
+<textarea crm-ui-richtext id="{{ fieldId }}" ng-model="getData()[fieldName]" ></textarea>
diff --git a/ext/afform/core/ang/afField/widgets/Select.html b/ext/afform/core/ang/afField/widgets/Select.html
new file mode 100644 (file)
index 0000000..298c16f
--- /dev/null
@@ -0,0 +1 @@
+<input crm-ui-select="{data: getOptions(), multiple: fieldDefn.input_attrs.multiple}" id="{{ fieldId }}" ng-model="getData()[fieldName]" />
diff --git a/ext/afform/core/ang/afField/widgets/Text.html b/ext/afform/core/ang/afField/widgets/Text.html
new file mode 100644 (file)
index 0000000..6f94d24
--- /dev/null
@@ -0,0 +1 @@
+<input class="crm-form-text" type="text" id="{{ fieldId }}" ng-model="getData()[fieldName]" />
diff --git a/ext/afform/core/ang/afField/widgets/TextArea.html b/ext/afform/core/ang/afField/widgets/TextArea.html
new file mode 100644 (file)
index 0000000..f19d4c9
--- /dev/null
@@ -0,0 +1 @@
+<textarea class="crm-form-textarea" id="{{ fieldId }}" ng-model="getData()[fieldName]" ></textarea>
index c93f3045025207c5d7b84885450a93ef62bb75fb..ff3e7a48f94e2a379f0203a1ecfb5cd9b1a87a33 100644 (file)
@@ -3,15 +3,15 @@
 // in CiviCRM. See also:
 // http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_angularModules
 
-return array(
-  'js' => array(
-    0 => 'ang/afformCore.js',
-    1 => 'ang/afformCore/*.js',
-    2 => 'ang/afformCore/*/*.js',
-  ),
-  'css' => array('ang/afformCore.css'),
-  'requires' => array('crmUi', 'crmUtil', 'api4'),
-  'partials' => array('ang/afformCore'),
-  'settings' => array(),
+return [
+  'js' => [
+    'ang/afformCore.js',
+    'ang/afformCore/*.js',
+    'ang/afformCore/*/*.js',
+  ],
+  'css' => ['ang/afformCore.css'],
+  'requires' => ['crmUi', 'crmUtil', 'api4', 'checklist-model'],
+  'partials' => ['ang/afformCore'],
+  'settings' => [],
   'basePages' => [],
-);
+];
index b583486da33bc7534028b2a421c7cec4c64e6948..9ee3732d3a16b61f5abe518bcb7efd63194dbd3f 100644 (file)
@@ -1,8 +1,6 @@
 (function(angular, $, _) {
   // Declare a list of dependencies.
-  angular.module('afformCore', [
-    'crmUi', 'crmUtil', 'ngRoute', 'api4'
-  ]);
+  angular.module('afformCore', CRM.angRequires('afformCore'));
 
   // Use `afformCoreDirective(string name)` to generate an AngularJS directive.
   angular.module('afformCore').service('afformCoreDirective', function($routeParams, crmApi4, crmStatus, crmUiAlert){
index 2fbf67d02fad6885f7756e8b8bd9f7136bc969ec..3b145af59c873a9b6159b1a6d25397b00ec00e8c 100644 (file)
@@ -3,16 +3,16 @@
 // in CiviCRM. See also:
 // http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_angularModules
 
-return array(
-  'js' => array(
+return [
+  'js' => [
     'ang/afformStandalone.js',
     'ang/afformStandalone/*.js',
     'ang/afformStandalone/*/*.js',
-  ),
-  'css' => array(),
-  'partials' => array(
+  ],
+  'css' => [],
+  'partials' => [
     'ang/afformStandalone',
-  ),
-  'settings' => array(),
+  ],
+  'settings' => [],
   'requires' => ['ngRoute'],
-);
+];
index 4beb737db3e9b6d6d5572d4a6cb881863429136a..aeaf3848e8cd4e731700c310b31533647d62dbc1 100644 (file)
@@ -1,9 +1,5 @@
-<h3>This new page is generated by CRM/Afform/Page/AfformBase.php</h3>
-
-{* Example: Display a variable directly *}
-<p>The current time is {$currentTime}</p>
-
-{* Example: Display a translated string -- which happens to include a variable *}
-<p>{ts 1=$currentTime}(In your native language) The current time is %1.{/ts}</p>
-
-<p>Please open "{$afform}"</p>
\ No newline at end of file
+{literal}
+  <div ng-app="crmApp">
+    <form ng-view></form>
+  </div>
+{/literal}