1 (function(angular
, $, _
) {
3 // Example usage: <div af-fieldset="myModel"><af-field name="do_not_email" /></div>
4 angular
.module('af').component('afField', {
6 afFieldset
: '^^afFieldset',
8 afRepeatItem
: '?^^afRepeatItem'
10 templateUrl
: '~/af/afField.html',
15 controller: function($scope
, $element
, crmApi4
, $timeout
) {
16 var ts
= $scope
.ts
= CRM
.ts('org.civicrm.afform'),
18 boolOptions
= [{id
: true, label
: ts('Yes')}, {id
: false, label
: ts('No')}],
19 // Only used for is_primary radio button
20 noOptions
= [{id
: true, label
: ''}];
22 // Attributes for each of the low & high date fields when using search_range
25 this.$onInit = function() {
26 var closestController
= $($element
).closest('[af-fieldset],[af-join],[af-repeat-item]');
27 $scope
.dataProvider
= closestController
.is('[af-repeat-item]') ? ctrl
.afRepeatItem
: ctrl
.afJoin
|| ctrl
.afFieldset
;
28 $scope
.fieldId
= ctrl
.fieldName
+ '-' + id
++;
30 $element
.addClass('af-field-type-' + _
.kebabCase(ctrl
.defn
.input_type
));
33 if (ctrl
.defn
.search_range
) {
34 // Initialize value as object unless using relative date select
35 var initialVal
= $scope
.dataProvider
.getFieldData()[ctrl
.fieldName
];
36 if (!_
.isArray($scope
.dataProvider
.getFieldData()[ctrl
.fieldName
]) &&
37 (ctrl
.defn
.input_type
!== 'Select' || !ctrl
.defn
.is_date
|| initialVal
!== '{}')
39 $scope
.dataProvider
.getFieldData()[ctrl
.fieldName
] = {};
41 // Initialize inputAttrs (only used for datePickers at the moment)
42 if (ctrl
.defn
.is_date
) {
43 this.inputAttrs
.push(ctrl
.defn
.input_attrs
|| {});
44 for (var i
= 1; i
<= 2; ++i
) {
45 var attrs
= _
.cloneDeep(ctrl
.defn
.input_attrs
|| {});
46 attrs
.placeholder
= attrs
['placeholder' + i
];
47 attrs
.timePlaceholder
= attrs
['timePlaceholder' + i
];
48 ctrl
.inputAttrs
.push(attrs
);
53 // is_primary field - watch others in this afRepeat block to ensure only one is selected
54 if (ctrl
.fieldName
=== 'is_primary' && 'repeatIndex' in $scope
.dataProvider
) {
55 $scope
.$watch('dataProvider.afRepeat.getEntityController().getData()', function (items
, prev
) {
56 var index
= $scope
.dataProvider
.repeatIndex
;
57 // Set first item to primary if there isn't a primary
58 if (items
&& !index
&& !_
.find(items
, 'is_primary')) {
59 $scope
.dataProvider
.getFieldData().is_primary
= true;
61 // Set this item to not primary if another has been selected
62 if (items
&& prev
&& items
.length
=== prev
.length
&& items
[index
].is_primary
&& prev
[index
].is_primary
&&
63 _
.filter(items
, 'is_primary').length
> 1
65 $scope
.dataProvider
.getFieldData().is_primary
= false;
70 // ChainSelect - watch control field & reload options as needed
71 if (ctrl
.defn
.input_type
=== 'ChainSelect') {
72 $scope
.$watch('dataProvider.getFieldData()[defn.input_attrs.control_field]', function(val
) {
75 where
: [['name', '=', ctrl
.fieldName
]],
77 loadOptions
: ['id', 'label'],
80 params
.values
[ctrl
.defn
.input_attrs
.control_field
] = val
;
81 crmApi4($scope
.dataProvider
.getEntityType(), 'getFields', params
, 0)
82 .then(function(data
) {
83 ctrl
.defn
.options
= data
.options
;
90 if (ctrl
.defn
.afform_default
) {
91 // Wait for parent controllers to initialize
93 $scope
.dataProvider
.getFieldData()[ctrl
.fieldName
] = ctrl
.defn
.afform_default
;
99 // Get the repeat index of the entity fieldset (not the join)
100 ctrl
.getEntityIndex = function() {
101 // If already in a join repeat, look up the outer repeat
102 if ('repeatIndex' in $scope
.dataProvider
&& $scope
.dataProvider
.afRepeat
.getRepeatType() === 'join') {
103 return $scope
.dataProvider
.outerRepeatItem
? $scope
.dataProvider
.outerRepeatItem
.repeatIndex
: 0;
105 return ctrl
.afRepeatItem
? ctrl
.afRepeatItem
.repeatIndex
: 0;
109 // Params for the Afform.submitFile API when uploading a file field
110 ctrl
.getFileUploadParams = function() {
112 entityName
: ctrl
.afFieldset
.modelName
,
113 fieldName
: ctrl
.fieldName
,
114 joinEntity
: ctrl
.afJoin
? ctrl
.afJoin
.entity
: null,
115 entityIndex
: ctrl
.getEntityIndex(),
116 joinIndex
: ctrl
.afJoin
&& $scope
.dataProvider
.repeatIndex
|| null
120 $scope
.getOptions = function () {
121 return ctrl
.defn
.options
|| (ctrl
.fieldName
=== 'is_primary' && ctrl
.defn
.input_type
=== 'Radio' ? noOptions
: boolOptions
);
124 $scope
.select2Options = function() {
126 results
: _
.transform($scope
.getOptions(), function(result
, opt
) {
127 result
.push({id
: opt
.id
, text
: opt
.label
});
132 // Getter/Setter function for fields of type select or entityRef.
133 $scope
.getSetSelect = function(val
) {
134 var currentVal
= $scope
.dataProvider
.getFieldData()[ctrl
.fieldName
];
136 if (arguments
.length
) {
137 if (ctrl
.defn
.is_date
) {
138 // The '{}' string is a placeholder for "choose date range"
140 val
= !_
.isPlainObject(currentVal
) ? {} : currentVal
;
143 // If search_range, this select is the "low" value (the high value uses ng-model without a getterSetter fn)
144 else if (ctrl
.defn
.search_range
) {
145 return ($scope
.dataProvider
.getFieldData()[ctrl
.fieldName
]['>='] = val
);
147 // A multi-select needs to split string value into an array
148 if (ctrl
.defn
.input_attrs
&& ctrl
.defn
.input_attrs
.multiple
) {
149 val
= val
? val
.split(',') : [];
151 return ($scope
.dataProvider
.getFieldData()[ctrl
.fieldName
] = val
);
154 if (_
.isArray(currentVal
)) {
155 return currentVal
.join(',');
157 if (ctrl
.defn
.is_date
) {
158 return _
.isPlainObject(currentVal
) ? '{}' : currentVal
;
160 // If search_range, this select is the "low" value (the high value uses ng-model without a getterSetter fn)
161 else if (ctrl
.defn
.search_range
) {
162 return currentVal
['>='];
169 })(angular
, CRM
.$, CRM
._
);