blob: d65884a5e0d2e8e1c6b5beb22f213c2dd38c9c38 [file] [log] [blame]
Matteo Scandoloa5d03d52016-07-21 11:35:46 -07001/**
2 * © OpenCORD
3 *
4 * Visit http://guide.xosproject.org/devguide/addview/ for more information
5 *
6 * Created by teone on 3/24/16.
7 */
8
9(function () {
10 'use strict';
11
12 angular.module('xos.uiComponents')
13
14 /**
15 * @ngdoc directive
16 * @name xos.uiComponents.directive:xosSmartTable
17 * @link xos.uiComponents.directive:xosTable xosTable
18 * @link xos.uiComponents.directive:xosForm xosForm
19 * @restrict E
20 * @description The xos-table directive
21 * @param {Object} config The configuration for the component,
22 * it is composed by the name of an angular [$resource](https://docs.angularjs.org/api/ngResource/service/$resource)
23 * and an array of fields that shouldn't be printed.
24 * ```
25 * {
26 resource: 'Users',
27 hiddenFields: []
28 }
29 * ```
30 * @scope
31 * @example
32
33 <example module="sampleSmartTable">
34 <file name="index.html">
35 <div ng-controller="SampleCtrl as vm">
36 <xos-smart-table config="vm.config"></xos-smart-table>
37 </div>
38 </file>
39 <file name="script.js">
40 angular.module('sampleSmartTable', ['xos.uiComponents', 'ngResource', 'ngMockE2E'])
41 // This is only for documentation purpose
42 .run(function($httpBackend, _){
43 let datas = [{id: 1, name: 'Jhon', surname: 'Doe'}];
44 let count = 1;
45
46 let paramsUrl = new RegExp(/\/test\/(.+)/);
47
48 $httpBackend.whenDELETE(paramsUrl, undefined, ['id']).respond((method, url, data, headers, params) => {
49 data = angular.fromJson(data);
50 let id = url.match(paramsUrl)[1];
51 _.remove(datas, (d) => {
52 return d.id === parseInt(id);
53 })
54 return [204];
55 });
56
57 $httpBackend.whenGET('/test').respond(200, datas)
58 $httpBackend.whenPOST('/test').respond((method, url, data) => {
59 data = angular.fromJson(data);
60 data.id = ++count;
61 datas.push(data);
62 return [201, data, {}];
63 });
64 })
65 .factory('_', function($window){
66 return $window._;
67 })
68 .service('SampleResource', function($resource){
69 return $resource('/test/:id', {id: '@id'});
70 })
71 // End of documentation purpose, example start
72 .controller('SampleCtrl', function(){
73 this.config = {
74 resource: 'SampleResource'
75 };
76 });
77 </file>
78 </example>
79 */
80
Matteo Scandolo1d689852016-09-29 09:42:12 -070081 .component('xosSmartTable', {
Matteo Scandoloa5d03d52016-07-21 11:35:46 -070082 restrict: 'E',
Arpit Agarwal34b63832016-08-08 11:59:45 -070083 bindings: {
Matteo Scandoloa5d03d52016-07-21 11:35:46 -070084 config: '='
85 },
86 template: `
87 <div class="row" ng-show="vm.data.length > 0">
88 <div class="col-xs-12 text-right">
89 <a href="" class="btn btn-success" ng-click="vm.createItem()">
90 Add
91 </a>
92 </div>
93 </div>
94 <div class="row">
95 <div class="col-xs-12 table-responsive">
96 <xos-table config="vm.tableConfig" data="vm.data"></xos-table>
97 </div>
98 </div>
99 <div class="panel panel-default" ng-show="vm.detailedItem">
100 <div class="panel-heading">
101 <div class="row">
102 <div class="col-xs-11">
103 <h3 class="panel-title" ng-show="vm.detailedItem.id">Update {{vm.config.resource}} {{vm.detailedItem.id}}</h3>
104 <h3 class="panel-title" ng-show="!vm.detailedItem.id">Create {{vm.config.resource}} item</h3>
105 </div>
106 <div class="col-xs-1">
107 <a href="" ng-click="vm.cleanForm()">
108 <i class="glyphicon glyphicon-remove pull-right"></i>
109 </a>
110 </div>
111 </div>
112 </div>
113 <div class="panel-body">
114 <xos-form config="vm.formConfig" ng-model="vm.detailedItem"></xos-form>
115 </div>
116 </div>
117 <xos-alert config="{type: 'success', closeBtn: true}" show="vm.responseMsg">{{vm.responseMsg}}</xos-alert>
118 <xos-alert config="{type: 'danger', closeBtn: true}" show="vm.responseErr">{{vm.responseErr}}</xos-alert>
119 `,
120 bindToController: true,
121 controllerAs: 'vm',
122 controller: function($injector, LabelFormatter, _, XosFormHelpers){
Arpit Agarwal34b63832016-08-08 11:59:45 -0700123
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700124 // TODO
125 // - Validate the config (what if resource does not exist?)
126
127 // NOTE
128 // Corner case
129 // - if response is empty, how can we generate a form ?
130
131 this.responseMsg = false;
132 this.responseErr = false;
133
134 this.tableConfig = {
135 columns: [
136 ],
137 actions: [
138 {
139 label: 'delete',
140 icon: 'remove',
141 cb: (item) => {
142 this.Resource.delete({id: item.id}).$promise
143 .then(() => {
144 _.remove(this.data, (d) => d.id === item.id);
145 this.responseMsg = `${this.config.resource} with id ${item.id} successfully deleted`;
146 })
147 .catch(err => {
148 this.responseErr = err.data.detail || `Error while deleting ${this.config.resource} with id ${item.id}`;
149 });
150 },
151 color: 'red'
152 },
153 {
154 label: 'details',
155 icon: 'search',
156 cb: (item) => {
157 this.detailedItem = item;
158 }
159 }
160 ],
161 classes: 'table table-striped table-bordered table-responsive',
162 filter: 'field',
163 order: true,
164 pagination: {
165 pageSize: 10
166 }
167 };
168
169 this.formConfig = {
170 exclude: this.config.hiddenFields,
171 fields: {},
172 formName: `${this.config.resource}Form`,
173 actions: [
174 {
175 label: 'Save',
176 icon: 'ok',
177 cb: (item) => {
178 let p;
179 let isNew = true;
180
181 if(item.id){
182 p = item.$update();
183 isNew = false;
184 }
185 else {
186 p = item.$save();
187 }
188
189 p.then((res) => {
190 if(isNew){
191 this.data.push(angular.copy(res));
192 }
193 delete this.detailedItem;
194 this.responseMsg = `${this.config.resource} with id ${item.id} successfully saved`;
195 })
196 .catch((err) => {
197 this.responseErr = err.data.detail || `Error while saving ${this.config.resource} with id ${item.id}`;
198 })
199 },
200 class: 'success'
201 }
202 ]
203 };
204
205 this.cleanForm = () => {
206 delete this.detailedItem;
207 };
208
209 this.createItem = () => {
210 this.detailedItem = new this.Resource();
211 };
212
213 this.Resource = $injector.get(this.config.resource);
214
215 const getData = () => {
216 this.Resource.query().$promise
217 .then((res) => {
218
219 if(!res[0]){
220 this.data = res;
221 return;
222 }
223
224 let item = res[0];
225 let props = Object.keys(item);
226
227 _.remove(props, p => {
228 return p === 'id' || p === 'validators'
229 });
230
231 // TODO move out cb, non sense triggering a lot of times
232 if(angular.isArray(this.config.hiddenFields)){
233 props = _.difference(props, this.config.hiddenFields)
234 }
235
Matteo Scandoloe57712f2016-09-21 15:27:36 -0700236 props.forEach(p => {
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700237 let fieldConfig = {
Matteo Scandoloe57712f2016-09-21 15:27:36 -0700238 label: LabelFormatter.format(p),
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700239 prop: p
240 };
241
Matteo Scandoloe57712f2016-09-21 15:27:36 -0700242 fieldConfig.type = XosFormHelpers._getFieldFormat(item[p]);
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700243
244 this.tableConfig.columns.push(fieldConfig);
245 });
246
247 // build form structure
248 // TODO move in a pure function for testing purposes
Matteo Scandolo1d689852016-09-29 09:42:12 -0700249 props.forEach((p) => {
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700250 this.formConfig.fields[p] = {
Matteo Scandoloe57712f2016-09-21 15:27:36 -0700251 label: LabelFormatter.format(p).replace(':', ''),
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700252 type: XosFormHelpers._getFieldFormat(item[p])
253 };
254 });
Matteo Scandoloa5d03d52016-07-21 11:35:46 -0700255 this.data = res;
256 });
257 }
258
259 getData();
260 }
Matteo Scandolo1d689852016-09-29 09:42:12 -0700261 });
262})();