blob: 4d53a925ce03ceb50c491eac22eeed55536dab62 [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
38 <example module="sampleAlert1">
39 <file name="index.html">
40 <div ng-controller="SampleCtrl1 as vm">
41
42 </div>
43 </file>
44 <file name="script.js">
45 angular.module('sampleAlert1', ['xos.uiComponents'])
46 .controller('SampleCtrl1', function(){
47 this.config1 = {
48 exclude: ['password', 'last_login']
49 };
50 });
51 </file>
52 </example>
53
54 **/
55
56 .directive('xosForm', function(){
57 return {
58 restrict: 'E',
59 scope: {
60 config: '=',
61 ngModel: '='
62 },
63 template: `
Matteo Scandolo4ba4cf12016-04-20 16:36:17 -070064 <ng-form name="vm.{{vm.config.formName || 'form'}}">
Matteo Scandolo6e2e6ff2016-04-20 14:59:39 -070065 <div class="form-group" ng-repeat="(name, field) in vm.formField">
Matteo Scandolo9f0e5ae2016-04-20 12:24:52 -070066 <label>{{field.label}}</label>
Matteo Scandolo4ba4cf12016-04-20 16:36:17 -070067 <input ng-if="field.type !== 'boolean'" type="{{field.type}}" name="{{name}}" class="form-control" ng-model="vm.ngModel[name]"/>
68 <span class="boolean-field" ng-if="field.type === 'boolean'">
69 <button
70 class="btn btn-success"
71 ng-show="vm.ngModel[name]"
72 ng-click="vm.ngModel[name] = false">
73 <i class="glyphicon glyphicon-ok"></i>
74 </button>
75 <button
76 class="btn btn-danger"
77 ng-show="!vm.ngModel[name]"
78 ng-click="vm.ngModel[name] = true">
79 <i class="glyphicon glyphicon-remove"></i>
80 </button>
81 </span>
82 <xos-validation errors="vm[vm.config.formName || 'form'][name].$error"></xos-validation>
Matteo Scandolo7bc39c42016-04-20 11:38:42 -070083 </div>
84 <div class="form-group" ng-if="vm.config.actions">
Matteo Scandolo4ba4cf12016-04-20 16:36:17 -070085 <button role="button" href=""
Matteo Scandolo7bc39c42016-04-20 11:38:42 -070086 ng-repeat="action in vm.config.actions"
87 ng-click="action.cb(vm.ngModel)"
88 class="btn btn-{{action.class}}"
89 title="{{action.label}}">
90 <i class="glyphicon glyphicon-{{action.icon}}"></i>
91 {{action.label}}
92 </button>
93 </div>
94 </ng-form>
95 `,
96 bindToController: true,
97 controllerAs: 'vm',
Matteo Scandolo9f0e5ae2016-04-20 12:24:52 -070098 controller: function($scope, $log, _, XosFormHelpers){
Matteo Scandolo7bc39c42016-04-20 11:38:42 -070099
100 if(!this.config){
101 throw new Error('[xosForm] Please provide a configuration via the "config" attribute');
102 }
103
104 if(!this.config.actions){
105 throw new Error('[xosForm] Please provide an action list in the configuration');
106 }
107
Matteo Scandolo7bc39c42016-04-20 11:38:42 -0700108 this.excludedField = ['id', 'validators', 'created', 'updated', 'deleted', 'backend_status'];
109 if(this.config && this.config.exclude){
110 this.excludedField = this.excludedField.concat(this.config.exclude);
111 }
112
113
114 this.formField = [];
115 $scope.$watch(() => this.ngModel, (model) => {
116 if(!model){
117 return;
118 }
Matteo Scandolo9f0e5ae2016-04-20 12:24:52 -0700119 this.formField = XosFormHelpers.buildFormStructure(XosFormHelpers.parseModelField(_.difference(Object.keys(model), this.excludedField)), this.config.fields, model);
Matteo Scandolo7bc39c42016-04-20 11:38:42 -0700120 });
121
122 }
123 }
124 })
125 .service('XosFormHelpers', function(_, LabelFormatter){
126
Matteo Scandolo6e2e6ff2016-04-20 14:59:39 -0700127 this._isEmail = (text) => {
128 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,}))/;
129 return re.test(text);
130 };
131
Matteo Scandolo7bc39c42016-04-20 11:38:42 -0700132 this._getFieldFormat = (value) => {
133
134 // check if is date
Matteo Scandolo9f0e5ae2016-04-20 12:24:52 -0700135 if (_.isDate(value) || (!Number.isNaN(Date.parse(value)) && Date.parse(value) > 0)){
Matteo Scandolo7bc39c42016-04-20 11:38:42 -0700136 return 'date';
137 }
138
139 // check if is boolean
140 // isNaN(false) = false, false is a number (0), true is a number (1)
141 if(typeof value === 'boolean'){
142 return 'boolean';
143 }
144
145 // check if a string is a number
146 if(!isNaN(value)){
147 return 'number';
148 }
149
Matteo Scandolo6e2e6ff2016-04-20 14:59:39 -0700150 // check if a string is an email
151 if(this._isEmail(value)){
152 return 'email';
153 }
154
Matteo Scandolo7bc39c42016-04-20 11:38:42 -0700155 return typeof value;
156 };
157
158 this.buildFormStructure = (modelField, customField, model) => {
159 return _.reduce(Object.keys(modelField), (form, f) => {
160 form[f] = {
161 label: (customField[f] && customField[f].label) ? `${customField[f].label}:` : LabelFormatter.format(f),
162 type: (customField[f] && customField[f].type) ? customField[f].type : this._getFieldFormat(model[f]),
163 validators: {}
164 };
Matteo Scandolo6e2e6ff2016-04-20 14:59:39 -0700165
166 if(form[f].type === 'date'){
167 model[f] = new Date(model[f]);
168 }
169
Matteo Scandolo4ba4cf12016-04-20 16:36:17 -0700170 if(form[f].type === 'number'){
171 model[f] = parseInt(model[f], 10);
172 }
173
Matteo Scandolo7bc39c42016-04-20 11:38:42 -0700174 return form;
175 }, {});
176 };
177
178 this.parseModelField = (fields) => {
179 return _.reduce(fields, (form, f) => {
180 form[f] = {};
181 return form;
182 }, {});
183 }
184
185 })
186})();