blob: 3a80fc16ccd010a154c9a89db3cd9b2ad814b5b0 [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 Scandolofb667b02016-04-20 16:36:17 -070088 <input ng-if="field.type !== 'boolean'" type="{{field.type}}" name="{{name}}" class="form-control" ng-model="vm.ngModel[name]"/>
89 <span class="boolean-field" ng-if="field.type === 'boolean'">
90 <button
91 class="btn btn-success"
92 ng-show="vm.ngModel[name]"
93 ng-click="vm.ngModel[name] = false">
94 <i class="glyphicon glyphicon-ok"></i>
95 </button>
96 <button
97 class="btn btn-danger"
98 ng-show="!vm.ngModel[name]"
99 ng-click="vm.ngModel[name] = true">
100 <i class="glyphicon glyphicon-remove"></i>
101 </button>
102 </span>
103 <xos-validation errors="vm[vm.config.formName || 'form'][name].$error"></xos-validation>
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700104 </div>
105 <div class="form-group" ng-if="vm.config.actions">
Matteo Scandolofb667b02016-04-20 16:36:17 -0700106 <button role="button" href=""
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700107 ng-repeat="action in vm.config.actions"
108 ng-click="action.cb(vm.ngModel)"
109 class="btn btn-{{action.class}}"
110 title="{{action.label}}">
111 <i class="glyphicon glyphicon-{{action.icon}}"></i>
112 {{action.label}}
113 </button>
114 </div>
115 </ng-form>
116 `,
117 bindToController: true,
118 controllerAs: 'vm',
Matteo Scandolo824a7cb2016-04-20 12:24:52 -0700119 controller: function($scope, $log, _, XosFormHelpers){
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700120
121 if(!this.config){
122 throw new Error('[xosForm] Please provide a configuration via the "config" attribute');
123 }
124
125 if(!this.config.actions){
126 throw new Error('[xosForm] Please provide an action list in the configuration');
127 }
128
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700129 this.excludedField = ['id', 'validators', 'created', 'updated', 'deleted', 'backend_status'];
130 if(this.config && this.config.exclude){
131 this.excludedField = this.excludedField.concat(this.config.exclude);
132 }
133
134
135 this.formField = [];
136 $scope.$watch(() => this.ngModel, (model) => {
137 if(!model){
138 return;
139 }
Matteo Scandolo824a7cb2016-04-20 12:24:52 -0700140 this.formField = XosFormHelpers.buildFormStructure(XosFormHelpers.parseModelField(_.difference(Object.keys(model), this.excludedField)), this.config.fields, model);
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700141 });
142
143 }
144 }
145 })
146 .service('XosFormHelpers', function(_, LabelFormatter){
147
Matteo Scandolob389f7a2016-04-20 14:59:39 -0700148 this._isEmail = (text) => {
149 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,}))/;
150 return re.test(text);
151 };
152
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700153 this._getFieldFormat = (value) => {
154
155 // check if is date
Matteo Scandolo824a7cb2016-04-20 12:24:52 -0700156 if (_.isDate(value) || (!Number.isNaN(Date.parse(value)) && Date.parse(value) > 0)){
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700157 return 'date';
158 }
159
160 // check if is boolean
161 // isNaN(false) = false, false is a number (0), true is a number (1)
162 if(typeof value === 'boolean'){
163 return 'boolean';
164 }
165
166 // check if a string is a number
167 if(!isNaN(value)){
168 return 'number';
169 }
170
Matteo Scandolob389f7a2016-04-20 14:59:39 -0700171 // check if a string is an email
172 if(this._isEmail(value)){
173 return 'email';
174 }
175
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700176 return typeof value;
177 };
178
179 this.buildFormStructure = (modelField, customField, model) => {
Matteo Scandolod702aff2016-04-22 10:53:49 -0700180
181 customField = customField || {};
182
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700183 return _.reduce(Object.keys(modelField), (form, f) => {
184 form[f] = {
185 label: (customField[f] && customField[f].label) ? `${customField[f].label}:` : LabelFormatter.format(f),
186 type: (customField[f] && customField[f].type) ? customField[f].type : this._getFieldFormat(model[f]),
187 validators: {}
188 };
Matteo Scandolob389f7a2016-04-20 14:59:39 -0700189
190 if(form[f].type === 'date'){
191 model[f] = new Date(model[f]);
192 }
193
Matteo Scandolofb667b02016-04-20 16:36:17 -0700194 if(form[f].type === 'number'){
195 model[f] = parseInt(model[f], 10);
196 }
197
Matteo Scandoloc49f53c2016-04-20 11:38:42 -0700198 return form;
199 }, {});
200 };
201
202 this.parseModelField = (fields) => {
203 return _.reduce(fields, (form, f) => {
204 form[f] = {};
205 return form;
206 }, {});
207 }
208
209 })
210})();