blob: 622952f57a1ad0e5545ae7361f3dfdfa7abd8f86 [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
81 .directive('xosSmartTable', function(){
82 return {
83 restrict: 'E',
84 scope: {
85 config: '='
86 },
87 template: `
88 <div class="row" ng-show="vm.data.length > 0">
89 <div class="col-xs-12 text-right">
90 <a href="" class="btn btn-success" ng-click="vm.createItem()">
91 Add
92 </a>
93 </div>
94 </div>
95 <div class="row">
96 <div class="col-xs-12 table-responsive">
97 <xos-table config="vm.tableConfig" data="vm.data"></xos-table>
98 </div>
99 </div>
100 <div class="panel panel-default" ng-show="vm.detailedItem">
101 <div class="panel-heading">
102 <div class="row">
103 <div class="col-xs-11">
104 <h3 class="panel-title" ng-show="vm.detailedItem.id">Update {{vm.config.resource}} {{vm.detailedItem.id}}</h3>
105 <h3 class="panel-title" ng-show="!vm.detailedItem.id">Create {{vm.config.resource}} item</h3>
106 </div>
107 <div class="col-xs-1">
108 <a href="" ng-click="vm.cleanForm()">
109 <i class="glyphicon glyphicon-remove pull-right"></i>
110 </a>
111 </div>
112 </div>
113 </div>
114 <div class="panel-body">
115 <xos-form config="vm.formConfig" ng-model="vm.detailedItem"></xos-form>
116 </div>
117 </div>
118 <xos-alert config="{type: 'success', closeBtn: true}" show="vm.responseMsg">{{vm.responseMsg}}</xos-alert>
119 <xos-alert config="{type: 'danger', closeBtn: true}" show="vm.responseErr">{{vm.responseErr}}</xos-alert>
120 `,
121 bindToController: true,
122 controllerAs: 'vm',
123 controller: function($injector, LabelFormatter, _, XosFormHelpers){
124
125 // TODO
126 // - Validate the config (what if resource does not exist?)
127
128 // NOTE
129 // Corner case
130 // - if response is empty, how can we generate a form ?
131
132 this.responseMsg = false;
133 this.responseErr = false;
134
135 this.tableConfig = {
136 columns: [
137 ],
138 actions: [
139 {
140 label: 'delete',
141 icon: 'remove',
142 cb: (item) => {
143 this.Resource.delete({id: item.id}).$promise
144 .then(() => {
145 _.remove(this.data, (d) => d.id === item.id);
146 this.responseMsg = `${this.config.resource} with id ${item.id} successfully deleted`;
147 })
148 .catch(err => {
149 this.responseErr = err.data.detail || `Error while deleting ${this.config.resource} with id ${item.id}`;
150 });
151 },
152 color: 'red'
153 },
154 {
155 label: 'details',
156 icon: 'search',
157 cb: (item) => {
158 this.detailedItem = item;
159 }
160 }
161 ],
162 classes: 'table table-striped table-bordered table-responsive',
163 filter: 'field',
164 order: true,
165 pagination: {
166 pageSize: 10
167 }
168 };
169
170 this.formConfig = {
171 exclude: this.config.hiddenFields,
172 fields: {},
173 formName: `${this.config.resource}Form`,
174 actions: [
175 {
176 label: 'Save',
177 icon: 'ok',
178 cb: (item) => {
179 let p;
180 let isNew = true;
181
182 if(item.id){
183 p = item.$update();
184 isNew = false;
185 }
186 else {
187 p = item.$save();
188 }
189
190 p.then((res) => {
191 if(isNew){
192 this.data.push(angular.copy(res));
193 }
194 delete this.detailedItem;
195 this.responseMsg = `${this.config.resource} with id ${item.id} successfully saved`;
196 })
197 .catch((err) => {
198 this.responseErr = err.data.detail || `Error while saving ${this.config.resource} with id ${item.id}`;
199 })
200 },
201 class: 'success'
202 }
203 ]
204 };
205
206 this.cleanForm = () => {
207 delete this.detailedItem;
208 };
209
210 this.createItem = () => {
211 this.detailedItem = new this.Resource();
212 };
213
214 this.Resource = $injector.get(this.config.resource);
215
216 const getData = () => {
217 this.Resource.query().$promise
218 .then((res) => {
219
220 if(!res[0]){
221 this.data = res;
222 return;
223 }
224
225 let item = res[0];
226 let props = Object.keys(item);
227
228 _.remove(props, p => {
229 return p === 'id' || p === 'validators'
230 });
231
232 // TODO move out cb, non sense triggering a lot of times
233 if(angular.isArray(this.config.hiddenFields)){
234 props = _.difference(props, this.config.hiddenFields)
235 }
236
237 let labels = props.map(p => LabelFormatter.format(p));
238
239 props.forEach((p, i) => {
240 let fieldConfig = {
241 label: labels[i],
242 prop: p
243 };
244
245 if(angular.isString(item[p]) && typeof item[p] !== 'undefined'){
246 fieldConfig.type = typeof item[p];
247 }
248
249 this.tableConfig.columns.push(fieldConfig);
250 });
251
252 // build form structure
253 // TODO move in a pure function for testing purposes
254 props.forEach((p, i) => {
255 this.formConfig.fields[p] = {
256 label: LabelFormatter.format(labels[i]).replace(':', ''),
257 type: XosFormHelpers._getFieldFormat(item[p])
258 };
259 });
260
261 this.data = res;
262 });
263 }
264
265 getData();
266 }
267 };
268 });
269})();