blob: 35fe286ae32f7ece3c61cd59eb5b050801e3966b [file] [log] [blame]
Matteo Scandolo7bc39c42016-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 Scandolo840260d2016-04-22 09:56:48 -070038 <example module="sampleForm">
Matteo Scandolo7bc39c42016-04-20 11:38:42 -070039 <file name="index.html">
Matteo Scandolo840260d2016-04-22 09:56:48 -070040 <div ng-controller="SampleCtrl as vm">
41 <xos-form ng-model="model" config="config"></xos-form>
Matteo Scandolo7bc39c42016-04-20 11:38:42 -070042 </div>
43 </file>
44 <file name="script.js">
Matteo Scandolo840260d2016-04-22 09:56:48 -070045 angular.module('sampleForm', ['xos.uiComponents'])
46 .controller('SampleCtrl', function(){
47 this.model = {
48
49 }
50 this.config = {
51 exclude: ['password', 'last_login'],
52 formName: 'sampleForm',
53 actions: [
54 {
55 label: 'Save',
56 icon: 'ok', // refers to bootstraps glyphicon
57 cb: (user) => { // receive the model
58 console.log(user);
59 },
60 class: 'success'
61 }
62 ]
Matteo Scandolo7bc39c42016-04-20 11:38:42 -070063 };
64 });
65 </file>
66 </example>
67
68 **/
69
70 .directive('xosForm', function(){
71 return {
72 restrict: 'E',
73 scope: {
74 config: '=',
75 ngModel: '='
76 },
77 template: `
Matteo Scandolo4ba4cf12016-04-20 16:36:17 -070078 <ng-form name="vm.{{vm.config.formName || 'form'}}">
Matteo Scandolo6e2e6ff2016-04-20 14:59:39 -070079 <div class="form-group" ng-repeat="(name, field) in vm.formField">
Matteo Scandolo9f0e5ae2016-04-20 12:24:52 -070080 <label>{{field.label}}</label>
Matteo Scandolo4ba4cf12016-04-20 16:36:17 -070081 <input ng-if="field.type !== 'boolean'" type="{{field.type}}" name="{{name}}" class="form-control" ng-model="vm.ngModel[name]"/>
82 <span class="boolean-field" ng-if="field.type === 'boolean'">
83 <button
84 class="btn btn-success"
85 ng-show="vm.ngModel[name]"
86 ng-click="vm.ngModel[name] = false">
87 <i class="glyphicon glyphicon-ok"></i>
88 </button>
89 <button
90 class="btn btn-danger"
91 ng-show="!vm.ngModel[name]"
92 ng-click="vm.ngModel[name] = true">
93 <i class="glyphicon glyphicon-remove"></i>
94 </button>
95 </span>
96 <xos-validation errors="vm[vm.config.formName || 'form'][name].$error"></xos-validation>
Matteo Scandolo7bc39c42016-04-20 11:38:42 -070097 </div>
98 <div class="form-group" ng-if="vm.config.actions">
Matteo Scandolo4ba4cf12016-04-20 16:36:17 -070099 <button role="button" href=""
Matteo Scandolo7bc39c42016-04-20 11:38:42 -0700100 ng-repeat="action in vm.config.actions"
101 ng-click="action.cb(vm.ngModel)"
102 class="btn btn-{{action.class}}"
103 title="{{action.label}}">
104 <i class="glyphicon glyphicon-{{action.icon}}"></i>
105 {{action.label}}
106 </button>
107 </div>
108 </ng-form>
109 `,
110 bindToController: true,
111 controllerAs: 'vm',
Matteo Scandolo9f0e5ae2016-04-20 12:24:52 -0700112 controller: function($scope, $log, _, XosFormHelpers){
Matteo Scandolo7bc39c42016-04-20 11:38:42 -0700113
114 if(!this.config){
115 throw new Error('[xosForm] Please provide a configuration via the "config" attribute');
116 }
117
118 if(!this.config.actions){
119 throw new Error('[xosForm] Please provide an action list in the configuration');
120 }
121
Matteo Scandolo7bc39c42016-04-20 11:38:42 -0700122 this.excludedField = ['id', 'validators', 'created', 'updated', 'deleted', 'backend_status'];
123 if(this.config && this.config.exclude){
124 this.excludedField = this.excludedField.concat(this.config.exclude);
125 }
126
127
128 this.formField = [];
129 $scope.$watch(() => this.ngModel, (model) => {
130 if(!model){
131 return;
132 }
Matteo Scandolo9f0e5ae2016-04-20 12:24:52 -0700133 this.formField = XosFormHelpers.buildFormStructure(XosFormHelpers.parseModelField(_.difference(Object.keys(model), this.excludedField)), this.config.fields, model);
Matteo Scandolo7bc39c42016-04-20 11:38:42 -0700134 });
135
136 }
137 }
138 })
139 .service('XosFormHelpers', function(_, LabelFormatter){
140
Matteo Scandolo6e2e6ff2016-04-20 14:59:39 -0700141 this._isEmail = (text) => {
142 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,}))/;
143 return re.test(text);
144 };
145
Matteo Scandolo7bc39c42016-04-20 11:38:42 -0700146 this._getFieldFormat = (value) => {
147
148 // check if is date
Matteo Scandolo9f0e5ae2016-04-20 12:24:52 -0700149 if (_.isDate(value) || (!Number.isNaN(Date.parse(value)) && Date.parse(value) > 0)){
Matteo Scandolo7bc39c42016-04-20 11:38:42 -0700150 return 'date';
151 }
152
153 // check if is boolean
154 // isNaN(false) = false, false is a number (0), true is a number (1)
155 if(typeof value === 'boolean'){
156 return 'boolean';
157 }
158
159 // check if a string is a number
160 if(!isNaN(value)){
161 return 'number';
162 }
163
Matteo Scandolo6e2e6ff2016-04-20 14:59:39 -0700164 // check if a string is an email
165 if(this._isEmail(value)){
166 return 'email';
167 }
168
Matteo Scandolo7bc39c42016-04-20 11:38:42 -0700169 return typeof value;
170 };
171
172 this.buildFormStructure = (modelField, customField, model) => {
173 return _.reduce(Object.keys(modelField), (form, f) => {
174 form[f] = {
175 label: (customField[f] && customField[f].label) ? `${customField[f].label}:` : LabelFormatter.format(f),
176 type: (customField[f] && customField[f].type) ? customField[f].type : this._getFieldFormat(model[f]),
177 validators: {}
178 };
Matteo Scandolo6e2e6ff2016-04-20 14:59:39 -0700179
180 if(form[f].type === 'date'){
181 model[f] = new Date(model[f]);
182 }
183
Matteo Scandolo4ba4cf12016-04-20 16:36:17 -0700184 if(form[f].type === 'number'){
185 model[f] = parseInt(model[f], 10);
186 }
187
Matteo Scandolo7bc39c42016-04-20 11:38:42 -0700188 return form;
189 }, {});
190 };
191
192 this.parseModelField = (fields) => {
193 return _.reduce(fields, (form, f) => {
194 form[f] = {};
195 return form;
196 }, {});
197 }
198
199 })
200})();