blob: 1fbc9d03f7d7e48ee51bbe25d80e637a67614712 [file] [log] [blame]
/**
* © OpenCORD
*
* Visit http://guide.xosproject.org/devguide/addview/ for more information
*
* Created by teone on 3/24/16.
*/
(function () {
'use strict';
angular.module('xos.uiComponents')
/**
* @ngdoc directive
* @name xos.uiComponents.directive:xosSmartTable
* @link xos.uiComponents.directive:xosTable xosTable
* @link xos.uiComponents.directive:xosForm xosForm
* @restrict E
* @description The xos-table directive
* @param {Object} config The configuration for the component,
* it is composed by the name of an angular [$resource](https://docs.angularjs.org/api/ngResource/service/$resource)
* and an array of fields that shouldn't be printed.
* ```
* {
resource: 'Users',
hiddenFields: []
}
* ```
* @scope
* @example
<example module="sampleSmartTable">
<file name="index.html">
<div ng-controller="SampleCtrl as vm">
<xos-smart-table config="vm.config"></xos-smart-table>
</div>
</file>
<file name="script.js">
angular.module('sampleSmartTable', ['xos.uiComponents', 'ngResource', 'ngMockE2E'])
// This is only for documentation purpose
.run(function($httpBackend, _){
let datas = [{id: 1, name: 'Jhon', surname: 'Doe'}];
let count = 1;
let paramsUrl = new RegExp(/\/test\/(.+)/);
$httpBackend.whenDELETE(paramsUrl, undefined, ['id']).respond((method, url, data, headers, params) => {
data = angular.fromJson(data);
let id = url.match(paramsUrl)[1];
_.remove(datas, (d) => {
return d.id === parseInt(id);
})
return [204];
});
$httpBackend.whenGET('/test').respond(200, datas)
$httpBackend.whenPOST('/test').respond((method, url, data) => {
data = angular.fromJson(data);
data.id = ++count;
datas.push(data);
return [201, data, {}];
});
})
.factory('_', function($window){
return $window._;
})
.service('SampleResource', function($resource){
return $resource('/test/:id', {id: '@id'});
})
// End of documentation purpose, example start
.controller('SampleCtrl', function(){
this.config = {
resource: 'SampleResource'
};
});
</file>
</example>
*/
.component('xosSmartTable', {
restrict: 'E',
bindings: {
config: '='
},
template: `
<div class="row" ng-show="vm.data.length > 0">
<div class="col-xs-12 text-right">
<a href="" class="btn btn-success" ng-click="vm.createItem()">
Add
</a>
</div>
</div>
<div class="row">
<div class="col-xs-12 table-responsive">
<xos-table config="vm.tableConfig" data="vm.data"></xos-table>
</div>
</div>
<div class="panel panel-default" ng-show="vm.detailedItem">
<div class="panel-heading">
<div class="row">
<div class="col-xs-11">
<h3 class="panel-title" ng-show="vm.detailedItem.id">Update {{vm.config.resource}} {{vm.detailedItem.id}}</h3>
<h3 class="panel-title" ng-show="!vm.detailedItem.id">Create {{vm.config.resource}} item</h3>
</div>
<div class="col-xs-1">
<a href="" ng-click="vm.cleanForm()">
<i class="glyphicon glyphicon-remove pull-right"></i>
</a>
</div>
</div>
</div>
<div class="panel-body">
<xos-form config="vm.formConfig" ng-model="vm.detailedItem"></xos-form>
</div>
</div>
<xos-alert config="{type: 'success', closeBtn: true}" show="vm.responseMsg">{{vm.responseMsg}}</xos-alert>
<xos-alert config="{type: 'danger', closeBtn: true}" show="vm.responseErr">{{vm.responseErr}}</xos-alert>
`,
bindToController: true,
controllerAs: 'vm',
controller: function($injector, LabelFormatter, _, XosFormHelpers){
// TODO
// - Validate the config (what if resource does not exist?)
// NOTE
// Corner case
// - if response is empty, how can we generate a form ?
this.responseMsg = false;
this.responseErr = false;
this.tableConfig = {
columns: [
],
actions: [
{
label: 'delete',
icon: 'remove',
cb: (item) => {
this.Resource.delete({id: item.id}).$promise
.then(() => {
_.remove(this.data, (d) => d.id === item.id);
this.responseMsg = `${this.config.resource} with id ${item.id} successfully deleted`;
})
.catch(err => {
this.responseErr = err.data.detail || `Error while deleting ${this.config.resource} with id ${item.id}`;
});
},
color: 'red'
},
{
label: 'details',
icon: 'search',
cb: (item) => {
this.detailedItem = item;
}
}
],
classes: 'table table-striped table-bordered table-responsive',
filter: 'field',
order: true,
pagination: {
pageSize: 10
}
};
this.formConfig = {
exclude: this.config.hiddenFields,
fields: {},
formName: `${this.config.resource}Form`,
actions: [
{
label: 'Save',
icon: 'ok',
cb: (item) => {
let p;
let isNew = true;
if(item.id){
p = item.$update();
isNew = false;
}
else {
p = item.$save();
}
p.then((res) => {
if(isNew){
this.data.push(angular.copy(res));
}
delete this.detailedItem;
this.responseMsg = `${this.config.resource} with id ${item.id} successfully saved`;
})
.catch((err) => {
this.responseErr = err.data.detail || `Error while saving ${this.config.resource} with id ${item.id}`;
})
},
class: 'success'
}
]
};
this.cleanForm = () => {
delete this.detailedItem;
};
this.createItem = () => {
this.detailedItem = new this.Resource();
};
this.Resource = $injector.get(this.config.resource);
const getData = () => {
this.Resource.query().$promise
.then((res) => {
if(!res[0]){
this.data = res;
return;
}
let item = res[0];
let props = Object.keys(item);
_.remove(props, p => {
return p === 'id' || p === 'validators'
});
// TODO move out cb, non sense triggering a lot of times
if(angular.isArray(this.config.hiddenFields)){
props = _.difference(props, this.config.hiddenFields)
}
let labels = props.map(p => LabelFormatter.format(p));
props.forEach((p, i) => {
let fieldConfig = {
label: labels[i],
prop: p
};
if(angular.isString(item[p]) && typeof item[p] !== 'undefined'){
fieldConfig.type = typeof item[p];
}
this.tableConfig.columns.push(fieldConfig);
});
// build form structure
// TODO move in a pure function for testing purposes
props.forEach((p, i) => {
this.formConfig.fields[p] = {
label: LabelFormatter.format(labels[i]).replace(':', ''),
type: XosFormHelpers._getFieldFormat(item[p])
};
});
this.data = res;
});
}
getData();
}
});
})();