* Future consideration: how to validate when it's part of a subform?
* `<af-model>` should reference a declared model.
* `<af-field field-defn="...">` should contain an object.
-* `<a>` should have `href` or `ng-click` or `aff-api4-action`
+* `<a>` should have `href` or `ng-click` or `af-api4-action`
* Accept a restricted subset of HTML (e.g. `p h1 h2 h3` but not `script` or `[onclick]`)
* Accept a restricted subset of BootstrapCSS
* Accept a restricted subset of Angular HTML
-.aff-api4-action-running {
+.af-api4-action-running {
cursor: not-allowed;
color: black;
}
-a.aff-api4-action-idle {
+a.af-api4-action-idle {
cursor: pointer;
}
(function(angular, $, _) {
- angular.module('afformCore').directive('affApi4Action', function($parse, crmStatus, crmApi4) {
+ angular.module('afformCore').directive('afApi4Action', function($parse, crmStatus, crmApi4) {
return {
restrict: 'A',
scope: {
- affApi4Action: '@',
+ afApi4Action: '@',
msgStart: '=',
msgError: '=',
msgSuccess: '=',
},
link: function($scope, $el, $attr) {
var ts = CRM.ts(null);
- function running(x) {$el.toggleClass('aff-api4-action-running', x).toggleClass('aff-api4-action-idle', !x);}
+ function running(x) {$el.toggleClass('af-api4-action-running', x).toggleClass('af-api4-action-idle', !x);}
running(false);
$el.click(function(){
- var parts = $parse($scope.affApi4Action)($scope.$parent);
+ var parts = $parse($scope.afApi4Action)($scope.$parent);
var msgs = {start: $scope.msgStart || ts('Submitting...'), success: $scope.msgSuccess, error: $scope.msgError};
running(true);
crmStatus(msgs, crmApi4(parts[0], parts[1], parts[2]))
-# aff-api4-action
+# af-api4-action
This directive is designed for invoking an action via APIv4. Much like
`ng-click`, one would use `api4-action` to add behavior to a button or link.
```html
<button
- aff-api4-action="['Contact','delete',{where:['id','=','100]}}]"
+ af-api4-action="['Contact','delete',{where:['id','=','100]}}]"
>Delete</button>
```
```html
<button
- aff-api4-action="['Contact','delete',{where:['id','=','100]}}]"
+ af-api4-action="['Contact','delete',{where:['id','=','100]}}]"
msg-start="ts('Deleting...')"
msg-success="ts('Deleted')"
on-success="crmUiAlert({text:'Received ' + response.length + ' items'})"
on-error="crmUiAlert({text:'Failure: ' + error})"
>Delete</button>
-<!-- Automated flag with aff-api4-action-{running -->
+<!-- Automated flag with af-api4-action-{running -->
```
### Styling
-The `aff-api4-action` element will have the follow classes
+The `af-api4-action` element will have the follow classes
toggled automatically:
-* `aff-api4-action-running`: User has clicked to fire the action, and action is still running.
-* `aff-api4-action-idle`: The action is not running.
+* `af-api4-action-running`: User has clicked to fire the action, and action is still running.
+* `af-api4-action-idle`: The action is not running.
(function(angular, $, _) {
- angular.module('afformCore').directive('affApi4Ctrl', function() {
+ angular.module('afformCore').directive('afApi4Ctrl', function() {
return {
restrict: 'EA',
scope: {
- affApi4Ctrl: '=',
- affApi4: '@',
- affApi4Refresh: '@',
+ afApi4Ctrl: '=',
+ afApi4: '@',
+ afApi4Refresh: '@',
onRefresh: '@'
},
- controllerAs: 'affApi4Ctrl',
+ controllerAs: 'afApi4Ctrl',
controller: function($scope, $parse, crmThrottle, crmApi4) {
var ctrl = this;
// CONSIDER: Trade-offs of upfront vs ongoing evaluation.
- var parts = $parse($scope.affApi4)($scope.$parent);
+ var parts = $parse($scope.afApi4)($scope.$parent);
ctrl.entity = parts[0];
ctrl.action = parts[1];
ctrl.params = parts[2];
});
};
- $scope.affApi4Ctrl = this;
+ $scope.afApi4Ctrl = this;
- var mode = $scope.affApi4Refresh ? $scope.affApi4Refresh : 'auto';
+ var mode = $scope.afApi4Refresh ? $scope.afApi4Refresh : 'auto';
switch (mode) {
case 'auto':
// Note: Do NOT watch '.result' or '.loading' - causes infinite reloads.
- $scope.$watchCollection('affApi4Ctrl.params', ctrl.refresh, true);
- $scope.$watch('affApi4Ctrl.index', ctrl.refresh, true);
- $scope.$watch('affApi4Ctrl.entity', ctrl.refresh, true);
- $scope.$watch('affApi4Ctrl.action', ctrl.refresh, true);
+ $scope.$watchCollection('afApi4Ctrl.params', ctrl.refresh, true);
+ $scope.$watch('afApi4Ctrl.index', ctrl.refresh, true);
+ $scope.$watch('afApi4Ctrl.entity', ctrl.refresh, true);
+ $scope.$watch('afApi4Ctrl.action', ctrl.refresh, true);
break;
case 'init': ctrl.refresh(); break;
case 'manual': break;
Within this extension, there are things which need updating/addressing:
-* Test coverage for key Angular directives (e.g. `aff-api4-ctrl`, `aff-api4-action`)
+* Test coverage for key Angular directives (e.g. `af-api4-ctrl`, `af-api4-action`)
* There are several `FIXME`/`TODO` declarations in the code for checking pre-conditions, reporting errors, handling edge-cases, etc.
* Although afforms can be used in AngularJS, they don't fully support tooling like `cv ang:html:list`
and `hook_civicrm_alterAngular` changesets. We'll need a core patch to allow that. (Ex: Define partials via callback.)
-<div aff-api4-ctrl="apiData" aff-api4="['Afform', 'get', {layoutFormat: 'html', where: [['name', '=', options.name]]}]">
+<div af-api4-ctrl="apiData" af-api4="['Afform', 'get', {layoutFormat: 'html', where: [['name', '=', options.name]]}]">
<div ng-if="apiData.result.length == 0">
{{ts('Failed to find requested form.')}}
<div>
<a ng-href="#/">{{ts('Back')}}</a>
|
- <a aff-api4-action="['Afform', 'update', {layoutFormat: 'html', where: [['name', '=', resultForm.name]], values:resultForm}]">{{ts('Save')}}</a>
+ <a af-api4-action="['Afform', 'update', {layoutFormat: 'html', where: [['name', '=', resultForm.name]], values:resultForm}]">{{ts('Save')}}</a>
<span ng-if="resultForm.server_route">
| <a target="_blank" ng-href="{{crmUrl(resultForm.server_route)}}">Open</a>
</span>
<div
- aff-api4="['Afform', 'get', {select: ['name','title','is_public','server_route'], orderBy: {name:'ASC'}}]"
- aff-api4-ctrl="listCtrl">
+ af-api4="['Afform', 'get', {select: ['name','title','is_public','server_route'], orderBy: {name:'ASC'}}]"
+ af-api4-ctrl="listCtrl">
<div ng-if="apiData.result.length == 0">
{{ts('There are no forms! Tell Aristotle!')}}
<td>{{availForm.is_public ? ts('Frontend') : ts('Backend')}}</td>
<td>
<!--<a ng-click="crmStatus({start: ts('Reverting...'), success: ts('Reverted')}, crmApi4('Afform', 'revert', {where: [['name', '=', availForm.name]]}))">{{ts('Revert')}}</a>-->
- <a aff-api4-action="['Afform', 'revert', {where: [['name','=', availForm.name]]}]"
+ <a af-api4-action="['Afform', 'revert', {where: [['name','=', availForm.name]]}]"
msg-start="ts('Reverting...')"
msg-success="ts('Reverted')"
>{{ts('Revert')}}</a>