2021-10-23 12:26:42 +00:00
|
|
|
import ngModule from '../../module';
|
|
|
|
import Component from '../../lib/component';
|
2021-10-26 15:16:01 +00:00
|
|
|
import {buildFilter} from 'vn-loopback/util/filter';
|
2021-10-23 12:26:42 +00:00
|
|
|
import './style.scss';
|
|
|
|
|
|
|
|
export default class SmartTable extends Component {
|
|
|
|
constructor($element, $, $transclude) {
|
|
|
|
super($element, $);
|
|
|
|
this.$transclude = $transclude;
|
2021-10-25 12:20:17 +00:00
|
|
|
this.sortCriteria = [];
|
2021-10-25 15:18:57 +00:00
|
|
|
this.autoSave = false;
|
2021-10-23 12:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
get model() {
|
|
|
|
return this._model;
|
|
|
|
}
|
|
|
|
|
|
|
|
set model(value) {
|
|
|
|
this._model = value;
|
|
|
|
if (value) {
|
|
|
|
this.$.model = value;
|
|
|
|
this.transclude();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-25 15:18:57 +00:00
|
|
|
get checkedRows() {
|
|
|
|
const model = this.model;
|
|
|
|
if (model && model.data)
|
|
|
|
return model.data.filter(row => row.$checked);
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2021-10-23 12:26:42 +00:00
|
|
|
registerColumns() {
|
|
|
|
const header = this.element.querySelector('thead > tr');
|
|
|
|
if (!header) return;
|
|
|
|
const columns = header.querySelectorAll('th');
|
|
|
|
|
|
|
|
// Click handler
|
|
|
|
for (let column of columns) {
|
|
|
|
const field = column.getAttribute('field');
|
|
|
|
if (field)
|
|
|
|
column.addEventListener('click', () => this.orderHandler(column));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creo que se puede hacer directamente desde ng-transclude
|
|
|
|
transclude() {
|
|
|
|
const slotTable = this.element.querySelector('#table');
|
|
|
|
this.$transclude(($clone, $scope) => {
|
|
|
|
const table = $clone[0];
|
|
|
|
$scope.hasChanges = this.hasChanges;
|
|
|
|
slotTable.appendChild(table);
|
|
|
|
this.registerColumns();
|
|
|
|
}, null, 'table');
|
|
|
|
}
|
|
|
|
|
|
|
|
orderHandler(element) {
|
|
|
|
const field = element.getAttribute('field');
|
2021-10-25 12:20:17 +00:00
|
|
|
const existingCriteria = this.sortCriteria.find(criteria => {
|
|
|
|
return criteria.field == field;
|
|
|
|
});
|
|
|
|
|
2021-10-26 07:18:31 +00:00
|
|
|
const isASC = existingCriteria && existingCriteria.sortType == 'ASC';
|
|
|
|
const isDESC = existingCriteria && existingCriteria.sortType == 'DESC';
|
|
|
|
|
2021-10-25 12:20:17 +00:00
|
|
|
if (!existingCriteria) {
|
|
|
|
this.sortCriteria.push({field: field, sortType: 'ASC'});
|
|
|
|
element.classList.remove('desc');
|
|
|
|
element.classList.add('asc');
|
|
|
|
}
|
|
|
|
|
2021-10-26 07:18:31 +00:00
|
|
|
if (isDESC) {
|
2021-10-25 12:20:17 +00:00
|
|
|
this.sortCriteria.splice(this.sortCriteria.findIndex(criteria => {
|
|
|
|
return criteria.field == field;
|
|
|
|
}), 1);
|
|
|
|
element.classList.remove('desc');
|
|
|
|
element.classList.remove('asc');
|
|
|
|
}
|
2021-10-26 15:16:01 +00:00
|
|
|
|
2021-10-26 07:18:31 +00:00
|
|
|
if (isASC) {
|
2021-10-25 12:20:17 +00:00
|
|
|
existingCriteria.sortType = 'DESC';
|
|
|
|
element.classList.remove('asc');
|
|
|
|
element.classList.add('desc');
|
|
|
|
}
|
|
|
|
|
|
|
|
this.applySort();
|
|
|
|
}
|
|
|
|
|
2021-10-26 15:16:01 +00:00
|
|
|
displaySearch() {
|
|
|
|
const header = this.element.querySelector('thead > tr');
|
|
|
|
if (!header) return;
|
|
|
|
|
|
|
|
const tbody = this.element.querySelector('tbody');
|
|
|
|
const columns = header.querySelectorAll('th');
|
|
|
|
|
|
|
|
const hasSearchRow = tbody.querySelector('tr#searchRow');
|
|
|
|
if (hasSearchRow) return hasSearchRow.remove();
|
|
|
|
|
|
|
|
const searchRow = document.createElement('tr');
|
|
|
|
searchRow.setAttribute('id', 'searchRow');
|
|
|
|
for (let column of columns) {
|
|
|
|
const field = column.getAttribute('field');
|
|
|
|
const cell = document.createElement('td');
|
|
|
|
if (field) {
|
|
|
|
const input = this.$compile(`
|
|
|
|
<vn-textfield
|
|
|
|
name="${field}"
|
|
|
|
ng-model="searchProps['${field}']"
|
|
|
|
ng-keydown="$ctrl.searchByColumn($event, this, '${field}')"
|
|
|
|
clear-disabled="true"
|
|
|
|
/>`)(this.$);
|
|
|
|
cell.appendChild(input[0]);
|
|
|
|
}
|
|
|
|
searchRow.appendChild(cell);
|
|
|
|
}
|
|
|
|
|
|
|
|
tbody.prepend(searchRow);
|
|
|
|
}
|
|
|
|
|
|
|
|
searchByColumn($event, scope, field) {
|
|
|
|
if ($event.key != 'Enter') return;
|
|
|
|
|
|
|
|
const searchCriteria = scope.searchProps[field];
|
|
|
|
const emptySearch = searchCriteria == '' || null;
|
|
|
|
|
|
|
|
const filters = this.filterSanitizer(field);
|
|
|
|
|
|
|
|
if (filters && filters.userFilter)
|
|
|
|
this.model.userFilter = filters.userFilter;
|
|
|
|
|
|
|
|
if (!emptySearch)
|
|
|
|
this.addFilter(field, scope.searchProps[field]);
|
|
|
|
else this.model.refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
addFilter(field, value) {
|
|
|
|
let where = {[field]: value};
|
|
|
|
|
|
|
|
if (this.exprBuilder) {
|
|
|
|
where = buildFilter(where, (param, value) =>
|
|
|
|
this.exprBuilder({param, value})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.model.addFilter({where});
|
|
|
|
}
|
|
|
|
|
2021-10-25 12:20:17 +00:00
|
|
|
applySort() {
|
|
|
|
let order = this.sortCriteria.map(criteria => `${criteria.field} ${criteria.sortType}`);
|
|
|
|
order = order.join(', ');
|
|
|
|
|
|
|
|
if (order)
|
|
|
|
this.model.order = order;
|
|
|
|
|
|
|
|
this.model.refresh();
|
2021-10-23 12:26:42 +00:00
|
|
|
}
|
|
|
|
|
2021-10-26 15:16:01 +00:00
|
|
|
filterSanitizer(field) {
|
|
|
|
// tenemos que eliminar ands vacios al ir borrando filtros
|
|
|
|
const userFilter = this.model.userFilter;
|
|
|
|
const userParams = this.model.userParams;
|
|
|
|
const where = userFilter && userFilter.where;
|
|
|
|
|
|
|
|
if (this.exprBuilder) {
|
|
|
|
const param = this.exprBuilder({
|
|
|
|
param: field,
|
|
|
|
value: null
|
|
|
|
});
|
|
|
|
if (param) [field] = Object.keys(param);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!where) return;
|
|
|
|
|
|
|
|
const whereKeys = Object.keys(where);
|
|
|
|
for (let key of whereKeys) {
|
|
|
|
removeProp(where, field, key);
|
|
|
|
|
|
|
|
if (!Object.keys(where))
|
|
|
|
delete userFilter.where;
|
|
|
|
}
|
|
|
|
|
|
|
|
function removeProp(instance, findProp, prop) {
|
|
|
|
if (prop == findProp)
|
|
|
|
delete instance[prop];
|
|
|
|
|
|
|
|
if (prop === 'and') {
|
|
|
|
for (let [index, param] of instance[prop].entries()) {
|
|
|
|
const [key] = Object.keys(param);
|
|
|
|
if (key == findProp)
|
|
|
|
instance[prop].splice(index, 1);
|
|
|
|
|
|
|
|
if (param[key] instanceof Array)
|
|
|
|
removeProp(param, field, key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {userFilter, userParams};
|
|
|
|
}
|
|
|
|
|
|
|
|
removeFilter(field) {
|
|
|
|
//
|
|
|
|
this.model.applyFilter(userFilter, userParams);
|
|
|
|
}
|
|
|
|
|
2021-10-23 12:26:42 +00:00
|
|
|
createRow() {
|
|
|
|
this.model.insert({
|
|
|
|
nickname: 'New row'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
deleteAll() {
|
2021-10-26 15:16:01 +00:00
|
|
|
for (let row of this.checkedRows)
|
2021-10-25 15:18:57 +00:00
|
|
|
this.model.removeRow(row);
|
2021-10-23 12:26:42 +00:00
|
|
|
|
|
|
|
if (this.autoSave)
|
2021-10-24 11:35:37 +00:00
|
|
|
this.saveAll();
|
2021-10-23 12:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
saveAll() {
|
|
|
|
const model = this.model;
|
2021-10-25 15:18:57 +00:00
|
|
|
|
2021-10-23 12:26:42 +00:00
|
|
|
if (!model.isChanged)
|
|
|
|
return this.vnApp.showError(this.$t('No changes to save'));
|
|
|
|
|
|
|
|
this.model.save()
|
|
|
|
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')));
|
|
|
|
}
|
|
|
|
|
|
|
|
hasChanges() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SmartTable.$inject = ['$element', '$scope', '$transclude'];
|
|
|
|
|
|
|
|
ngModule.vnComponent('smartTable', {
|
|
|
|
template: require('./index.html'),
|
|
|
|
controller: SmartTable,
|
|
|
|
transclude: {
|
|
|
|
table: '?slotTable',
|
|
|
|
actions: '?slotActions'
|
|
|
|
},
|
|
|
|
bindings: {
|
|
|
|
model: '<?',
|
2021-10-26 15:16:01 +00:00
|
|
|
autoSave: '<?',
|
|
|
|
exprBuilder: '&?'
|
2021-10-23 12:26:42 +00:00
|
|
|
}
|
|
|
|
});
|