blob: 0f0f8f66562bfca2453f1fc4d1413ca2e6fa91e9 [file] [log] [blame]
Matteo Scandoloc49f53c2016-04-20 11:38:42 -07001/**
2 * © OpenCORD
3 *
4 * Visit http://guide.xosproject.org/devguide/addview/ for more information
5 *
6 * Created by teone on 4/18/16.
7 */
8
9(function () {
10 'use strict';
11
12 angular.module('xos.uiComponents')
13
14 /**
15 * @ngdoc directive
16 * @name xos.uiComponents.directive:xosForm
17 * @restrict E
18 * @description The xos-form directive
19 * @param {Object} config The configuration object
20 * ```
21 * {
22 * exclude: ['id', 'validators', 'created', 'updated', 'deleted'], //field to be skipped in the form, the provide values are concatenated
23 * actions: [ // define the form buttons with related callback
24 * {
25 label: 'save',
26 icon: 'ok', // refers to bootstraps glyphicon
27 cb: (user) => { // receive the model
28 console.log(user);
29 },
30 class: 'success'
31 }
32 * ]
33 * }
34 * ```
35 * @element ANY
36 * @scope
37 * @example
Matteo Scandolo9a8f53c2016-04-22 09:56:48 -070038 <example module="sampleForm">
Matteo Scandoloc49f53c2016-04-20 11:38:42 -070039 <file name="index.html">
Matteo Scandolo9a8f53c2016-04-22 09:56:48 -070040 <div ng-controller="SampleCtrl as vm">
Matteo Scandolod702aff2016-04-22 10:53:49 -070041 <xos-form ng-model="vm.model" config="vm.config"></xos-form>
Matteo Scandoloc49f53c2016-04-20 11:38:42 -070042 </div>
43 </file>
44 <file name="script.js">
Matteo Scandolo9a8f53c2016-04-22 09:56:48 -070045 angular.module('sampleForm', ['xos.uiComponents'])
Matteo Scandolod702aff2016-04-22 10:53:49 -070046 .factory('_', function($window){
47 return $window._;
48 })
Matteo Scandolo9a8f53c2016-04-22 09:56:48 -070049 .controller('SampleCtrl', function(){
50 this.model = {
Matteo Scandolod702aff2016-04-22 10:53:49 -070051 first_name: 'Jhon',
52 last_name: 'Doe',
53 email: 'jhon.doe@sample.com',
54 active: true,
55 birthDate: '2015-02-17T22:06:38.059000Z'
Matteo Scandolo9a8f53c2016-04-22 09:56:48 -070056 }
57 this.config = {
58 exclude: ['password', 'last_login'],
59 formName: 'sampleForm',
60 actions: [
61 {
62 label: 'Save',
63 icon: 'ok', // refers to bootstraps glyphicon
64 cb: (user) => { // receive the model
65 console.log(user);
66 },
67 class: 'success'
68 }
69 ]
Matteo Scandoloc49f53c2016-04-20 11:38:42 -070070 };
71 });
72 </file>
73 </example>
74
75 **/
76
77 .directive('xosForm', function(){
78 return {
79 restrict: 'E',
80 scope: {
81 config: '=',
82 ngModel: '='
83 },
84 template: `
Matteo Scandolofb667b02016-04-20 16:36:17 -070085 <ng-form name="vm.{{vm.config.formName || 'form'}}">
Matteo Scandolob389f7a2016-04-20 14:59:39 -070086 <div class="form-group" ng-repeat="(name, field) in vm.formField">
Matteo Scandolo824a7cb2016-04-20 12:24:52 -070087 <label>{{field.label}}</label>
Matteo Scandolo28e49b72016-04-22 14:14:03 -070088 <input
89 ng-if="field.type !== 'boolean'"
90 type="{{field.type}}"
91 name="{{name}}"
92 class="form-control"
93 ng-model="vm.ngModel[name]"
94 ng-minlength="field.validators.minlength || 0"
95 ng-maxlength="field.validators.maxlength || 2000"
Matteo Scandolo953ddad2016-04-25 08:15:24 -070096 ng-required="field.validators.required || false" />
Matteo Scandolofb667b02016-04-20 16:36:17 -070097 <span class="boolean-field" ng-if="field.type === 'boolean'">
98 <button
99 class="btn btn-success"
100 ng-show="vm.ngModel[name]"
101 ng-click="vm.ngModel[name] = false">
102 <i class="glyphicon glyphicon-ok"></i>
103 </button>
104 <button
105 class="btn btn-danger"
106 ng-show="!vm.ngModel[name]"
107 ng-click="vm.ngModel[name] = true">
108 <i class="glyphicon glyphicon-remove"></i>
109 </button>
110 </span>
Matteo Scandolo28e49b72016-04-22 14:14:03 -0700111 <!-- <pre>{{vm[vm.config.formName][name].$error | json}}</pre> -->
Matteo Scandolofb667b02016-04-20 16:36:17 -0700112 <xos-validation errors="vm[vm.config.formName || 'form'][name].$error"></xos-validation>
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700113 </div>
114 <div class="form-group" ng-if="vm.config.actions">
Matteo Scandolofb667b02016-04-20 16:36:17 -0700115 <button role="button" href=""
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700116 ng-repeat="action in vm.config.actions"
117 ng-click="action.cb(vm.ngModel)"
118 class="btn btn-{{action.class}}"
119 title="{{action.label}}">
120 <i class="glyphicon glyphicon-{{action.icon}}"></i>
121 {{action.label}}
122 </button>
123 </div>
124 </ng-form>
125 `,
126 bindToController: true,
127 controllerAs: 'vm',
Matteo Scandolo824a7cb2016-04-20 12:24:52 -0700128 controller: function($scope, $log, _, XosFormHelpers){
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700129
130 if(!this.config){
131 throw new Error('[xosForm] Please provide a configuration via the "config" attribute');
132 }
133
134 if(!this.config.actions){
135 throw new Error('[xosForm] Please provide an action list in the configuration');
136 }
137
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700138 this.excludedField = ['id', 'validators', 'created', 'updated', 'deleted', 'backend_status'];
139 if(this.config && this.config.exclude){
140 this.excludedField = this.excludedField.concat(this.config.exclude);
141 }
142
143
144 this.formField = [];
145 $scope.$watch(() => this.ngModel, (model) => {
146 if(!model){
147 return;
148 }
Matteo Scandolo824a7cb2016-04-20 12:24:52 -0700149 this.formField = XosFormHelpers.buildFormStructure(XosFormHelpers.parseModelField(_.difference(Object.keys(model), this.excludedField)), this.config.fields, model);
Matteo Scandolo28e49b72016-04-22 14:14:03 -0700150 }, true);
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700151
152 }
153 }
154 })
155 .service('XosFormHelpers', function(_, LabelFormatter){
156
Matteo Scandolob389f7a2016-04-20 14:59:39 -0700157 this._isEmail = (text) => {
158 var re = /(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))/;
159 return re.test(text);
160 };
161
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700162 this._getFieldFormat = (value) => {
163
164 // check if is date
Matteo Scandolo824a7cb2016-04-20 12:24:52 -0700165 if (_.isDate(value) || (!Number.isNaN(Date.parse(value)) && Date.parse(value) > 0)){
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700166 return 'date';
167 }
168
169 // check if is boolean
170 // isNaN(false) = false, false is a number (0), true is a number (1)
171 if(typeof value === 'boolean'){
172 return 'boolean';
173 }
174
175 // check if a string is a number
Matteo Scandolo28e49b72016-04-22 14:14:03 -0700176 if(!isNaN(value) && value !== null){
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700177 return 'number';
178 }
179
Matteo Scandolob389f7a2016-04-20 14:59:39 -0700180 // check if a string is an email
181 if(this._isEmail(value)){
182 return 'email';
183 }
184
Matteo Scandolo28e49b72016-04-22 14:14:03 -0700185 // if null return string
186 if(value === null){
187 return 'string';
188 }
189
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700190 return typeof value;
191 };
192
193 this.buildFormStructure = (modelField, customField, model) => {
Matteo Scandolod702aff2016-04-22 10:53:49 -0700194
195 customField = customField || {};
196
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700197 return _.reduce(Object.keys(modelField), (form, f) => {
Matteo Scandolo28e49b72016-04-22 14:14:03 -0700198
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700199 form[f] = {
200 label: (customField[f] && customField[f].label) ? `${customField[f].label}:` : LabelFormatter.format(f),
201 type: (customField[f] && customField[f].type) ? customField[f].type : this._getFieldFormat(model[f]),
Matteo Scandolo28e49b72016-04-22 14:14:03 -0700202 validators: (customField[f] && customField[f].validators) ? customField[f].validators : {}
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700203 };
Matteo Scandolob389f7a2016-04-20 14:59:39 -0700204
205 if(form[f].type === 'date'){
206 model[f] = new Date(model[f]);
207 }
208
Matteo Scandolofb667b02016-04-20 16:36:17 -0700209 if(form[f].type === 'number'){
210 model[f] = parseInt(model[f], 10);
211 }
212
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700213 return form;
214 }, {});
215 };
216
217 this.parseModelField = (fields) => {
218 return _.reduce(fields, (form, f) => {
219 form[f] = {};
220 return form;
221 }, {});
222 }
223
224 })
225})();