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
, $location
) {
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
;
89 // Wait for parent controllers to initialize
91 // Unique field name = entity_name index . join . field_name
92 var entityName
= ctrl
.afFieldset
.getName(),
93 joinEntity
= ctrl
.afJoin
? ctrl
.afJoin
.entity
: null,
95 urlArgs
= $location
.search();
97 var index
= ctrl
.getEntityIndex();
98 uniquePrefix
= entityName
+ (index
? index
+ 1 : '') + (joinEntity
? '.' + joinEntity
: '') + '.';
100 // Set default value based on url
101 if (urlArgs
&& urlArgs
[uniquePrefix
+ ctrl
.fieldName
]) {
102 setValue(urlArgs
[uniquePrefix
+ ctrl
.fieldName
]);
104 // Set default value based on field defn
105 else if (ctrl
.defn
.afform_default
) {
106 setValue(ctrl
.defn
.afform_default
);
111 // Set default value; ensure data type matches input type
112 function setValue(value
) {
113 if (ctrl
.defn
.input_type
=== 'Number' && ctrl
.defn
.search_range
) {
114 if (!_
.isPlainObject(value
)) {
116 '>=': +(('' + value
).split('-')[0] || 0),
117 '<=': +(('' + value
).split('-')[1] || 0),
120 } else if (ctrl
.defn
.input_type
=== 'Number') {
122 } else if (ctrl
.defn
.search_range
&& !_
.isPlainObject(value
)) {
124 '>=': ('' + value
).split('-')[0],
125 '<=': ('' + value
).split('-')[1] || '',
129 $scope
.dataProvider
.getFieldData()[ctrl
.fieldName
] = value
;
132 // Get the repeat index of the entity fieldset (not the join)
133 ctrl
.getEntityIndex = function() {
134 // If already in a join repeat, look up the outer repeat
135 if ('repeatIndex' in $scope
.dataProvider
&& $scope
.dataProvider
.afRepeat
.getRepeatType() === 'join') {
136 return $scope
.dataProvider
.outerRepeatItem
? $scope
.dataProvider
.outerRepeatItem
.repeatIndex
: 0;
138 return ctrl
.afRepeatItem
? ctrl
.afRepeatItem
.repeatIndex
: 0;
142 // Params for the Afform.submitFile API when uploading a file field
143 ctrl
.getFileUploadParams = function() {
145 entityName
: ctrl
.afFieldset
.modelName
,
146 fieldName
: ctrl
.fieldName
,
147 joinEntity
: ctrl
.afJoin
? ctrl
.afJoin
.entity
: null,
148 entityIndex
: ctrl
.getEntityIndex(),
149 joinIndex
: ctrl
.afJoin
&& $scope
.dataProvider
.repeatIndex
|| null
153 $scope
.getOptions = function () {
154 return ctrl
.defn
.options
|| (ctrl
.fieldName
=== 'is_primary' && ctrl
.defn
.input_type
=== 'Radio' ? noOptions
: boolOptions
);
157 $scope
.select2Options = function() {
159 results
: _
.transform($scope
.getOptions(), function(result
, opt
) {
160 result
.push({id
: opt
.id
, text
: opt
.label
});
165 // Getter/Setter function for fields of type select or entityRef.
166 $scope
.getSetSelect = function(val
) {
167 var currentVal
= $scope
.dataProvider
.getFieldData()[ctrl
.fieldName
];
169 if (arguments
.length
) {
170 if (ctrl
.defn
.is_date
) {
171 // The '{}' string is a placeholder for "choose date range"
173 val
= !_
.isPlainObject(currentVal
) ? {} : currentVal
;
176 // If search_range, this select is the "low" value (the high value uses ng-model without a getterSetter fn)
177 else if (ctrl
.defn
.search_range
) {
178 return ($scope
.dataProvider
.getFieldData()[ctrl
.fieldName
]['>='] = val
);
180 // A multi-select needs to split string value into an array
181 if (ctrl
.defn
.input_attrs
&& ctrl
.defn
.input_attrs
.multiple
) {
182 val
= val
? val
.split(',') : [];
184 return ($scope
.dataProvider
.getFieldData()[ctrl
.fieldName
] = val
);
187 if (_
.isArray(currentVal
)) {
188 return currentVal
.join(',');
190 if (ctrl
.defn
.is_date
) {
191 return _
.isPlainObject(currentVal
) ? '{}' : currentVal
;
193 // If search_range, this select is the "low" value (the high value uses ng-model without a getterSetter fn)
194 else if (ctrl
.defn
.search_range
) {
195 return currentVal
['>='];
202 })(angular
, CRM
.$, CRM
._
);