blob: c417433973c55b331597698086d2c3b02dcf32f9 [file] [log] [blame]
Matteo Scandolo686547a2017-08-08 13:05:25 -07001
2/*
3 * Copyright 2017-present Open Networking Foundation
4
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8
9 * http://www.apache.org/licenses/LICENSE-2.0
10
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
Matteo Scandoloa5d03d52016-07-21 11:35:46 -070019/**
20 * © OpenCORD
21 *
22 * Visit http://guide.xosproject.org/devguide/addview/ for more information
23 *
24 * Created by teone on 4/18/16.
25 */
26
27(function () {
28 'use strict';
29
30
31 angular.module('xos.uiComponents')
32
33 /**
34 * @ngdoc directive
35 * @name xos.uiComponents.directive:xosForm
36 * @restrict E
37 * @description The xos-form directive.
38 * This components have two usage, given a model it is able to autogenerate a form or it can be configured to create a custom form.
39 * @param {Object} config The configuration object
40 * ```
41 * {
42 * exclude: ['id', 'validators', 'created', 'updated', 'deleted'], //field to be skipped in the form, the provide values are concatenated
Matteo Scandolobfb8ebd2016-10-28 17:53:05 +020043 * order: ['field1', 'field2'], // ordering the fields (missing ones are attached at the end)
Matteo Scandoloa5d03d52016-07-21 11:35:46 -070044 * actions: [ // define the form buttons with related callback
45 * {
46 label: 'save',
47 icon: 'ok', // refers to bootstraps glyphicon
48 cb: (user) => { // receive the model
49 console.log(user);
50 },
51 class: 'success'
52 }
53 * ],
54 * feedback: {
55 show: false,
56 message: 'Form submitted successfully !!!',
57 type: 'success' //refers to bootstrap class
58 },
59 * fields: {
60 * field_name: {
61 * label: 'Field Label',
62 * type: 'string' // options are: [date, boolean, number, email, string, select],
63 * validators: {
64 * minlength: number,
65 maxlength: number,
66 required: boolean,
67 min: number,
68 max: number,
69 custom: (value) => {
70 // do your validation here and return true | false
71 // alternatively you can return an array [errorName, true|false]
72 }
73 * }
74 * }
75 * }
76 * }
77 * ```
Matteo Scandolobfb8ebd2016-10-28 17:53:05 +020078 * @param {Object} ngModel The model object (it is mandatory to specify at least an empty object)
Matteo Scandoloa5d03d52016-07-21 11:35:46 -070079 * @element ANY
80 * @scope
81 * @requires xos.uiComponents.directive:xosField
82 * @requires xos.uiComponents.XosFormHelpers
83 * @requires xos.helpers._
84 * @example
Steven Burrows84818482016-09-29 15:33:46 -070085
Matteo Scandoloa5d03d52016-07-21 11:35:46 -070086 Autogenerated form
87
88 <example module="sampleForm">
89 <file name="script.js">
90 angular.module('sampleForm', ['xos.uiComponents'])
91 .factory('_', function($window){
92 return $window._;
93 })
94 .controller('SampleCtrl', function(){
95 this.model = {
96 first_name: 'Jhon',
97 last_name: 'Doe',
98 email: 'jhon.doe@sample.com',
99 active: true,
100 birthDate: '2015-02-17T22:06:38.059000Z'
101 }
102 this.config = {
103 exclude: ['password', 'last_login'],
104 formName: 'sampleForm',
105 actions: [
106 {
107 label: 'Save',
108 icon: 'ok', // refers to bootstraps glyphicon
109 cb: (user) => { // receive the model
110 console.log(user);
111 },
112 class: 'success'
113 }
114 ]
115 };
116 });
117 </file>
118 <file name="index.html">
119 <div ng-controller="SampleCtrl as vm">
120 <xos-form ng-model="vm.model" config="vm.config"></xos-form>
121 </div>
122 </file>
123 </example>
124
125 Configuration defined form
126
127 <example module="sampleForm1">
128 <file name="script.js">
129 angular.module('sampleForm1', ['xos.uiComponents','ngResource', 'ngMockE2E'])
130 .factory('_', function($window){
131 return $window._;
132 })
133 .controller('SampleCtrl1', function(SampleResource){
134
Matteo Scandolobfb8ebd2016-10-28 17:53:05 +0200135 this.model = {};
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700136
137 this.config = {
138 exclude: ['password', 'last_login'],
139 formName: 'sampleForm1',
140 feedback: {
141 show: false,
Matteo Scandolobfb8ebd2016-10-28 17:53:05 +0200142 message: 'Form submitted successfully!',
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700143 type: 'success'
144 },
145 actions: [
146 {
147 label: 'Save',
Matteo Scandolobfb8ebd2016-10-28 17:53:05 +0200148 icon: 'ok',
149 cb: (user) => {
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700150 console.log(user);
151 this.config.feedback.show = true;
152 this.config.feedback.type='success';
153 },
154 class: 'success'
155 }
156 ],
Matteo Scandolobfb8ebd2016-10-28 17:53:05 +0200157 order: ['site', 'last_name', 'first_name', 'age'],
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700158 fields: {
159 first_name: {
160 type: 'string',
161 validators: {
162 required: true
163 }
164 },
165 last_name: {
166 label: 'Surname',
167 type: 'string',
168 validators: {
169 required: true,
170 minlength: 10
171 }
172 },
173 age: {
174 type: 'number',
175 validators: {
176 required: true,
177 min: 21
178 }
179 },
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700180 site: {
Matteo Scandolobfb8ebd2016-10-28 17:53:05 +0200181 label: 'Site',
182 type: 'select',
183 validators: { required: true},
184 hint: 'The Site this Slice belongs to',
185 options: []
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700186 },
187 }
188 };
189 SampleResource.query().$promise
Matteo Scandolobfb8ebd2016-10-28 17:53:05 +0200190 .then((users) => {
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700191 this.optionVal = users;
192 this.config.fields['site'].options = this.optionVal;
Matteo Scandolobfb8ebd2016-10-28 17:53:05 +0200193 })
194 .catch((e) => {
195 throw new Error(e);
196 });
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700197 });
198 </file>
199 <file name="backend.js">
200 angular.module('sampleForm1')
201 .run(function($httpBackend, _){
202 let datas = [{id: 1, label: 'site1'},{id: 4, label: 'site4'},{id: 3, label: 'site3'}];
203 let paramsUrl = new RegExp(/\/test\/(.+)/);
204 $httpBackend.whenGET('/test').respond(200, datas)
205 })
206 .service('SampleResource', function($resource){
207 return $resource('/test/:id', {id: '@id'});
208 });
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700209 </file>
210 <file name="index.html">
211 <div ng-controller="SampleCtrl1 as vm">
212 <xos-form ng-model="vm.model" config="vm.config"></xos-form>
213 </div>
214 </file>
215 </example>
216
217 **/
218
Arpit Agarwal34b63832016-08-08 11:59:45 -0700219 .component('xosForm', {
220 restrict: 'E',
221 bindings: {
222 config: '=',
223 ngModel: '='
224 },
225 template: `
226 <form name="vm.{{vm.config.formName || 'form'}}" novalidate>
227 <div class="form-group" ng-repeat="(name, field) in vm.formField">
228 <xos-field name="name" field="field" ng-model="vm.ngModel[name]"></xos-field>
229 <xos-validation field="vm[vm.config.formName || 'form'][name]" form = "vm[vm.config.formName || 'form']"></xos-validation>
230 <div class="alert alert-info" ng-show="(field.hint).length >0" role="alert">{{field.hint}}</div>
231 </div>
232 <div class="form-group" ng-if="vm.config.actions">
233 <xos-alert config="vm.config.feedback" show="vm.config.feedback.show">{{vm.config.feedback.message}}</xos-alert>
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700234
Arpit Agarwal34b63832016-08-08 11:59:45 -0700235 <button role="button" href=""
236 ng-repeat="action in vm.config.actions"
237 ng-click="action.cb(vm.ngModel, vm[vm.config.formName || 'form'])"
238 class="btn btn-{{action.class}}"
239 title="{{action.label}}">
240 <i class="glyphicon glyphicon-{{action.icon}}"></i>
241 {{action.label}}
242 </button>
243 </div>
244 </form>
245 `,
246 bindToController: true,
247 controllerAs: 'vm',
248 controller: function($scope, $log, _, XosFormHelpers){
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700249
Arpit Agarwal34b63832016-08-08 11:59:45 -0700250 if(!this.config){
251 throw new Error('[xosForm] Please provide a configuration via the "config" attribute');
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700252 }
Arpit Agarwal34b63832016-08-08 11:59:45 -0700253
254 if(!this.config.actions){
255 throw new Error('[xosForm] Please provide an action list in the configuration');
256 }
257
258 if(!this.config.feedback){
259 this.config.feedback = {
260 show: false,
261 message: 'Form submitted successfully !!!',
262 type: 'success'
263 }
264 }
265
266 this.excludedField = ['id', 'validators', 'created', 'updated', 'deleted', 'backend_status'];
267 if(this.config && this.config.exclude){
268 this.excludedField = this.excludedField.concat(this.config.exclude);
269 }
270
271 this.formField = [];
272
273 $scope.$watch(() => this.config, ()=> {
274 if(!this.ngModel){
275 return;
276 }
277 let diff = _.difference(Object.keys(this.ngModel), this.excludedField);
278 let modelField = XosFormHelpers.parseModelField(diff);
Matteo Scandolobfb8ebd2016-10-28 17:53:05 +0200279 this.formField = XosFormHelpers.buildFormStructure(modelField, this.config.fields, this.ngModel, this.config.order);
Arpit Agarwal34b63832016-08-08 11:59:45 -0700280 }, true);
281
282 $scope.$watch(() => this.ngModel, (model) => {
283 // empty from old stuff
284 this.formField = {};
285 if(!model){
286 return;
287 }
288 let diff = _.difference(Object.keys(model), this.excludedField);
289 let modelField = XosFormHelpers.parseModelField(diff);
Steven Burrows84818482016-09-29 15:33:46 -0700290 this.formField = XosFormHelpers.buildFormStructure(modelField, this.config.fields, model, this.config.order);
Arpit Agarwal34b63832016-08-08 11:59:45 -0700291 });
292
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700293 }
294 });
295})();