blob: 6b413bb37c9d30165774bde8a1e935ddf88802d6 [file] [log] [blame]
Matteo Scandoloa5d03d52016-07-21 11:35:46 -07001/**
2 * © OpenCORD
3 *
4 * Visit http://guide.xosproject.org/devguide/addview/ for more information
5 *
6 * Created by teone on 5/25/16.
7 */
8
9(function () {
10 'use strict';
11
12 angular.module('xos.uiComponents')
13 /**
14 * @ngdoc directive
15 * @name xos.uiComponents.directive:xosField
16 * @restrict E
17 * @description The xos-field directive.
18 * This component decide, give a field wich kind of input it need to print.
19 * @param {string} name The field name
20 * @param {object} field The field configuration:
21 * ```
22 * {
23 * label: 'Label',
24 * type: 'number', //typeof field
25 * validators: {} // see xosForm for more details
26 * }
27 * ```
28 * @param {mixed} ngModel The field value
29 *
30 * @example
31
32 # Basic Example
33
34 <example module="sampleField1">
35 <file name="script.js">
36 angular.module('sampleField1', ['xos.uiComponents'])
37 .factory('_', function($window){
38 return $window._;
39 })
40 .controller('SampleCtrl', function(){
41 this.name = 'input-name';
42 this.field = {label: 'My String Value:', type: 'string'};
43 this.model = 'my string';
44 });
45 </file>
46 <file name="index.html">
47 <div ng-controller="SampleCtrl as vm">
48 <xos-field ng-model="vm.model" name="vm.name" field="vm.field"></xos-field>
49 </div>
50 </file>
51 </example>
52
53 # Possible Values
54
55 <example module="sampleField2">
56 <file name="script.js">
57 angular.module('sampleField2', ['xos.uiComponents'])
58 .factory('_', function($window){
59 return $window._;
60 })
61 .controller('SampleCtrl', function(){
62 this.field1 = {
63 name: 'number-field',
64 field: {label: 'My Number Value:', type: 'number'},
65 model: 2
66 };
67
68 this.field2 = {
69 name: 'date-field',
70 field: {label: 'My Date Value:', type: 'date'},
71 model: new Date()
72 };
73
74 this.field3 = {
75 name: 'boolean-field',
76 field: {label: 'My Boolean Value:', type: 'boolean'},
77 model: true
78 };
79
80 this.field4 = {
81 name: 'email-field',
82 field: {label: 'My Email Value:', type: 'email'},
83 model: 'sample@domain.us'
84 };
Matteo Scandolo65116c42016-09-21 17:06:23 -070085
86 this.field5 = {
87 name: 'select',
Matteo Scandolobfb8ebd2016-10-28 17:53:05 +020088 field: {
89 label: 'Select field:',
90 type: 'select',
91 options: [
92 {id: 1, label: 'One'},
93 {id: 2, label: 'Two'},
94 {id: 3, label: 'Three'},
95 ]
96 },
97 model: 1
98 };
99
100 this.arrayField = {
101 name: 'array',
102 field: {
103 label: 'Array field:',
104 type: 'array',
105 options: ['one', 'two', 'three', 'four']
106 },
107 model: ['one', 'two'],
Matteo Scandolo65116c42016-09-21 17:06:23 -0700108 };
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700109 });
110 </file>
111 <file name="index.html">
112 <div ng-controller="SampleCtrl as vm">
113 <xos-field ng-model="vm.field1.model" name="vm.field1.name" field="vm.field1.field"></xos-field>
114 <xos-field ng-model="vm.field2.model" name="vm.field2.name" field="vm.field2.field"></xos-field>
115 <xos-field ng-model="vm.field3.model" name="vm.field3.name" field="vm.field3.field"></xos-field>
116 <xos-field ng-model="vm.field4.model" name="vm.field4.name" field="vm.field4.field"></xos-field>
Matteo Scandolo65116c42016-09-21 17:06:23 -0700117 <xos-field ng-model="vm.field5.model" name="vm.field5.name" field="vm.field5.field"></xos-field>
Matteo Scandolobfb8ebd2016-10-28 17:53:05 +0200118 <xos-field ng-model="vm.arrayField.model" name="vm.arrayField.name" field="vm.arrayField.field"></xos-field>
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700119 </div>
120 </file>
121 </example>
122
123 # This element is recursive
124
125 <example module="sampleField3">
126 <file name="script.js">
127 angular.module('sampleField3', ['xos.uiComponents'])
128 .factory('_', function($window){
129 return $window._;
130 })
131 .controller('SampleCtrl', function(){
132 this.name1 = 'input-name';
133 this.field1 = {label: 'My Object Field:', type: 'object'};
134 this.model1 = {
135 name: 'Jhon',
136 age: '25',
137 email: 'jhon@thewall.ru',
138 active: true
139 };
140
141 this.name2 = 'another-name';
142 this.field2 = {
143 label: 'Empty Object Field',
144 type: 'object',
145 properties: {
146 foo: {
147 label: 'FooLabel:',
148 type: 'string',
149 validators: {
150 required: true
151 }
152 },
153 bar: {
154 type: 'number'
155 }
156 }
157 }
158 });
159 </file>
160 <file name="index.html">
161 <div ng-controller="SampleCtrl as vm">
162 <h4>Autogenerated object field</h4>
163 <xos-field ng-model="vm.model1" name="vm.name1" field="vm.field1"></xos-field>
164
165 <h4>Configured object field</h4>
166 <xos-field ng-model="vm.model2" name="vm.name2" field="vm.field2"></xos-field>
167 </div>
168 </file>
169 </example>
170 */
Arpit Agarwal34b63832016-08-08 11:59:45 -0700171 .component('xosField', {
172 restrict: 'E',
173 bindings: {
174 name: '=',
175 field: '=',
176 ngModel: '='
177 },
178 template: `
Matteo Scandolo65116c42016-09-21 17:06:23 -0700179 <label ng-if="vm.field.type !== 'object' && vm.field.type !== 'array'">{{vm.field.label}}</label>
180 <input
181 xos-custom-validator custom-validator="vm.field.validators.custom || null"
182 ng-if="vm.field.type !== 'boolean' && vm.field.type !== 'object' && vm.field.type !== 'select' && vm.field.type !== 'array'"
183 type="{{vm.field.type}}"
184 name="{{vm.name}}"
185 class="form-control"
186 ng-model="vm.ngModel"
187 ng-minlength="vm.field.validators.minlength || 0"
188 ng-maxlength="vm.field.validators.maxlength || 2000"
189 ng-required="vm.field.validators.required || false" />
190 <select class="form-control" ng-if ="vm.field.type === 'select'"
191 name = "{{vm.name}}"
192 ng-options="item.id as item.label for item in vm.field.options"
193 ng-model="vm.ngModel"
194 ng-required="vm.field.validators.required || false">
195 </select>
196 <span class="boolean-field" ng-if="vm.field.type === 'boolean'">
197 <a href="#"
198 class="btn btn-success"
199 ng-show="vm.ngModel"
200 ng-click="vm.ngModel = false">
201 <i class="glyphicon glyphicon-ok"></i>
202 </a>
203 <a href="#"
204 class="btn btn-danger"
205 ng-show="!vm.ngModel"
206 ng-click="vm.ngModel = true">
207 <i class="glyphicon glyphicon-remove"></i>
208 </a>
209 </span>
210 <div
211 class="panel panel-default object-field"
212 ng-if="vm.field.type == 'object' && (!vm.isEmptyObject(vm.ngModel) || !vm.isEmptyObject(vm.field.properties))"
213 >
214 <div class="panel-heading">{{vm.field.label}}</div>
215 <div class="panel-body">
216 <div ng-if="!vm.field.properties" ng-repeat="(k, v) in vm.ngModel">
217 <xos-field
218 name="k"
219 field="{label: vm.formatLabel(k), type: vm.getType(v)}"
220 ng-model="v">
221 </xos-field>
Arpit Agarwal34b63832016-08-08 11:59:45 -0700222 </div>
Matteo Scandolo65116c42016-09-21 17:06:23 -0700223 <div ng-if="vm.field.properties" ng-repeat="(k, v) in vm.field.properties">
224 <xos-field
225 name="k"
226 field="{
227 label: v.label || vm.formatLabel(k),
228 type: v.type,
229 validators: v.validators
230 }"
231 ng-model="vm.ngModel[k]">
232 </xos-field>
233 </div>
234 </div>
235 </div>
236 <div
237 class="panel panel-default array-field"
238 ng-if="vm.field.type == 'array'">
239 <div class="panel-heading">{{vm.field.label}}</div>
240 <div class="panel-body selected">
241 <ul class="draggable" dnd-list="vm.ngModel">
242 <li
243 class="array-element"
244 ng-repeat="item in vm.ngModel"
245 dnd-draggable="item"
246 dnd-moved="vm.ngModel.splice($index, 1)"
247 dnd-effect-allowed="move"
248 dnd-selected="models.selected = item"
249 >
250 <div class="well well-sm text-center">
251 {{item}}
252 </div>
253 </li>
254 <div class="clearfix"></div>
255 </ul>
256 </div>
257 <div class="panel-body unselected">
258 <ul class="draggable" dnd-list="vm.field.availableOptions">
259 <li
260 class="array-element"
261 ng-repeat="item in vm.field.availableOptions"
262 dnd-draggable="item"
263 dnd-moved="vm.field.availableOptions.splice($index, 1)"
264 dnd-effect-allowed="move"
265 dnd-selected="models.selected = item"
266 >
267 <div class="well well-sm text-center">
268 {{item}}
269 </div>
270 </li>
271 <div class="clearfix"></div>
272 </ul>
273 </div>
274 </div>
Arpit Agarwal34b63832016-08-08 11:59:45 -0700275 `,
276 bindToController: true,
277 controllerAs: 'vm',
Matteo Scandolo65116c42016-09-21 17:06:23 -0700278 controller: function($attrs, $scope, XosFormHelpers, LabelFormatter, _){
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700279
Arpit Agarwal34b63832016-08-08 11:59:45 -0700280 if(!this.name){
281 throw new Error('[xosField] Please provide a field name');
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700282 }
Arpit Agarwal34b63832016-08-08 11:59:45 -0700283 if(!this.field){
284 throw new Error('[xosField] Please provide a field definition');
285 }
286 if(!this.field.type){
287 throw new Error('[xosField] Please provide a type in the field definition');
288 }
289 if(!$attrs.ngModel){
290 throw new Error('[xosField] Please provide an ng-model');
291 }
292 this.getType = XosFormHelpers._getFieldFormat;
293 this.formatLabel = LabelFormatter.format;
294
295 this.isEmptyObject = o => o ? Object.keys(o).length === 0 : true;
Matteo Scandolo65116c42016-09-21 17:06:23 -0700296
297 if(this.field.type === 'array'){
298 $scope.$watch(() => this.ngModel.length, () => {
299 this.field.availableOptions = _.difference(this.field.options, this.ngModel);
300 });
301 }
302
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700303 }
304 })
305
306/**
307 * @ngdoc directive
308 * @name xos.uiComponents.directive:xosCustomValidator
309 * @restrict A
310 * @description The xosCustomValidator directive.
311 * This component apply a custom validation function
312 * @param {function} customValidator The function that execute the validation.
313 *
314 * You should do your validation here and return true | false,
315 * or alternatively you can return an array [errorName, true|false]
316 */
317 .directive('xosCustomValidator', function(){
318 return {
319 restrict: 'A',
320 scope: {
321 fn: '=customValidator'
322 },
323 require: 'ngModel',
324 link: function(scope, element, attr, ctrl){
325 if(!angular.isFunction(scope.fn)){
326 return;
327 }
328
329 function customValidatorWrapper(ngModelValue) {
330 const valid = scope.fn(ngModelValue);
331 if(angular.isArray(valid)){
332 // ES6 spread rocks over fn.apply()
333 ctrl.$setValidity(...valid);
334 }
335 else{
336 ctrl.$setValidity('custom', valid);
337 }
338 return ngModelValue;
339 }
340
341 ctrl.$parsers.push(customValidatorWrapper);
342 }
343 };
344 });
345})();