2018-05-31 09:52:39 +00:00
|
|
|
import ngModule from '../../module';
|
|
|
|
import ModelProxy from '../model-proxy/model-proxy';
|
2018-12-27 11:54:16 +00:00
|
|
|
import {mergeWhere, mergeFilters} from 'vn-loopback/util/filter';
|
2018-05-31 09:52:39 +00:00
|
|
|
|
2018-10-23 10:14:47 +00:00
|
|
|
/**
|
|
|
|
* Model that uses remote loopback model as datasource.
|
|
|
|
*
|
|
|
|
* @property {Array<String>} fields the list of fields to fetch
|
|
|
|
*/
|
2018-05-31 09:52:39 +00:00
|
|
|
export default class CrudModel extends ModelProxy {
|
2018-10-22 06:23:10 +00:00
|
|
|
constructor($q, $http, $element, $scope) {
|
|
|
|
super($element, $scope);
|
2018-05-31 09:52:39 +00:00
|
|
|
this.$http = $http;
|
|
|
|
this.$q = $q;
|
|
|
|
this.primaryKey = 'id';
|
2018-12-19 14:59:35 +00:00
|
|
|
this.autoLoad = false;
|
2022-12-20 08:48:41 +00:00
|
|
|
this.page = 1;
|
2018-05-31 09:52:39 +00:00
|
|
|
}
|
|
|
|
|
2018-10-18 07:24:20 +00:00
|
|
|
$onInit() {
|
2018-10-22 06:23:10 +00:00
|
|
|
this.initialized = true;
|
2018-10-18 07:24:20 +00:00
|
|
|
this.autoRefresh();
|
|
|
|
}
|
|
|
|
|
2018-06-07 21:47:19 +00:00
|
|
|
get isLoading() {
|
|
|
|
return this.canceler != null;
|
|
|
|
}
|
|
|
|
|
2018-10-23 10:14:47 +00:00
|
|
|
/**
|
|
|
|
* @type {String} The remote model URL
|
|
|
|
*/
|
|
|
|
get url() {
|
|
|
|
return this._url;
|
|
|
|
}
|
|
|
|
|
2018-10-18 07:24:20 +00:00
|
|
|
set url(url) {
|
|
|
|
if (this._url === url) return;
|
|
|
|
this._url = url;
|
|
|
|
this.clear();
|
2018-10-22 06:23:10 +00:00
|
|
|
|
|
|
|
if (this.initialized)
|
|
|
|
this.autoRefresh();
|
2018-10-18 07:24:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
autoRefresh() {
|
2018-05-31 09:52:39 +00:00
|
|
|
if (this.autoLoad)
|
2018-10-18 07:24:20 +00:00
|
|
|
return this.refresh();
|
|
|
|
return this.$q.resolve();
|
2018-05-31 09:52:39 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 11:01:21 +00:00
|
|
|
buildFilter() {
|
|
|
|
let order = this.order;
|
|
|
|
|
|
|
|
if (typeof order === 'string')
|
|
|
|
order = this.order.split(/\s*,\s*/);
|
2018-05-31 09:52:39 +00:00
|
|
|
|
2018-06-07 21:47:19 +00:00
|
|
|
let myFilter = {
|
|
|
|
fields: this.fields,
|
|
|
|
where: mergeWhere(this.link, this.where),
|
|
|
|
include: this.include,
|
2018-09-05 11:01:21 +00:00
|
|
|
order: order,
|
2018-07-09 16:06:25 +00:00
|
|
|
limit: this.limit
|
2018-06-07 21:47:19 +00:00
|
|
|
};
|
|
|
|
|
2018-05-31 09:52:39 +00:00
|
|
|
let filter = this.filter;
|
2018-06-07 21:47:19 +00:00
|
|
|
filter = mergeFilters(myFilter, filter);
|
2018-09-05 11:01:21 +00:00
|
|
|
filter = mergeFilters(this.userFilter, filter);
|
|
|
|
return filter;
|
2018-06-07 21:47:19 +00:00
|
|
|
}
|
2018-05-31 09:52:39 +00:00
|
|
|
|
2018-09-05 11:01:21 +00:00
|
|
|
refresh() {
|
2018-10-18 07:24:20 +00:00
|
|
|
if (!this._url)
|
|
|
|
return this.$q.resolve();
|
2018-09-05 11:01:21 +00:00
|
|
|
return this.sendRequest(this.buildFilter());
|
2018-06-07 21:47:19 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 11:01:21 +00:00
|
|
|
/**
|
|
|
|
* Applies a new filter to the model.
|
|
|
|
*
|
2018-10-23 10:14:47 +00:00
|
|
|
* @param {Object} filter The Loopback filter
|
|
|
|
* @param {Object} params Custom parameters
|
2018-09-05 11:01:21 +00:00
|
|
|
* @return {Promise} The request promise
|
|
|
|
*/
|
2018-10-23 10:14:47 +00:00
|
|
|
applyFilter(filter, params) {
|
|
|
|
this.userFilter = filter;
|
|
|
|
this.userParams = params;
|
2018-09-05 11:01:21 +00:00
|
|
|
return this.refresh();
|
2018-06-07 21:47:19 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 11:01:21 +00:00
|
|
|
/**
|
|
|
|
* Adds a filter to the model.
|
|
|
|
*
|
2018-10-23 10:14:47 +00:00
|
|
|
* @param {Object} filter The Loopback filter
|
|
|
|
* @param {Object} params Custom parameters
|
2018-09-05 11:01:21 +00:00
|
|
|
* @return {Promise} The request promise
|
|
|
|
*/
|
2018-10-23 10:14:47 +00:00
|
|
|
addFilter(filter, params) {
|
|
|
|
this.userFilter = mergeFilters(filter, this.userFilter);
|
2019-10-28 16:31:33 +00:00
|
|
|
this.userParams = Object.assign({}, this.userParams, params);
|
2018-09-05 11:01:21 +00:00
|
|
|
return this.refresh();
|
2018-06-07 21:47:19 +00:00
|
|
|
}
|
2018-05-31 09:52:39 +00:00
|
|
|
|
2022-09-29 10:36:16 +00:00
|
|
|
/**
|
|
|
|
* Applies a new filter to the model.
|
|
|
|
*
|
|
|
|
* @param {Object} params Custom parameters
|
|
|
|
* @return {Promise} The request promise
|
|
|
|
*/
|
|
|
|
|
|
|
|
applyParams(params) {
|
|
|
|
this.userParams = params;
|
|
|
|
return this.refresh();
|
|
|
|
}
|
|
|
|
|
2018-09-05 11:01:21 +00:00
|
|
|
removeFilter() {
|
2020-02-17 12:50:54 +00:00
|
|
|
return this.applyFilter(null, null);
|
2018-06-07 21:47:19 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 11:01:21 +00:00
|
|
|
/**
|
|
|
|
* Cancels the current request, if any.
|
|
|
|
*/
|
|
|
|
cancelRequest() {
|
|
|
|
if (this.canceler) {
|
|
|
|
this.canceler.resolve();
|
|
|
|
this.canceler = null;
|
|
|
|
}
|
2018-06-07 21:47:19 +00:00
|
|
|
}
|
2018-05-31 09:52:39 +00:00
|
|
|
|
2023-05-08 14:50:20 +00:00
|
|
|
loadMore(append) {
|
2018-10-18 07:24:20 +00:00
|
|
|
if (!this.moreRows)
|
|
|
|
return this.$q.resolve();
|
|
|
|
|
2022-12-20 08:48:41 +00:00
|
|
|
const filter = Object.assign({}, this.currentFilter);
|
2023-05-08 14:50:20 +00:00
|
|
|
if (append)
|
2022-12-20 08:48:41 +00:00
|
|
|
filter.skip = this.orgData ? this.orgData.length : 0;
|
|
|
|
|
2023-05-08 14:50:20 +00:00
|
|
|
if (!append) {
|
2022-12-20 08:48:41 +00:00
|
|
|
this.page += 1;
|
|
|
|
filter.limit = this.page * this.limit;
|
|
|
|
}
|
|
|
|
|
2023-05-11 10:07:14 +00:00
|
|
|
return this.sendRequest(filter, append);
|
2018-10-18 07:24:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
clear() {
|
|
|
|
this.orgData = null;
|
2018-11-29 07:09:40 +00:00
|
|
|
this.moreRows = null;
|
2018-05-31 09:52:39 +00:00
|
|
|
}
|
|
|
|
|
2022-12-29 13:24:32 +00:00
|
|
|
getChanges() {
|
|
|
|
if (!this.isChanged) return null;
|
2018-10-18 07:24:20 +00:00
|
|
|
|
2022-12-29 13:24:32 +00:00
|
|
|
const deletes = [];
|
|
|
|
const updates = [];
|
|
|
|
const creates = [];
|
2018-05-31 09:52:39 +00:00
|
|
|
|
2022-12-29 13:24:32 +00:00
|
|
|
const pk = this.primaryKey;
|
2018-10-19 17:54:46 +00:00
|
|
|
|
2022-12-29 13:24:32 +00:00
|
|
|
for (let row of this.removed)
|
2018-11-02 14:44:27 +00:00
|
|
|
deletes.push(row.$orgRow[pk]);
|
2018-05-31 09:52:39 +00:00
|
|
|
|
2020-02-06 06:41:51 +00:00
|
|
|
for (let row of this.data) {
|
2018-10-18 07:24:20 +00:00
|
|
|
if (row.$isNew) {
|
|
|
|
let data = {};
|
2018-11-29 07:09:40 +00:00
|
|
|
for (let prop in row) {
|
2018-10-18 07:24:20 +00:00
|
|
|
if (prop.charAt(0) !== '$')
|
|
|
|
data[prop] = row[prop];
|
2018-11-29 07:09:40 +00:00
|
|
|
}
|
2020-02-06 06:41:51 +00:00
|
|
|
creates.push(row);
|
2018-10-18 07:24:20 +00:00
|
|
|
} else if (row.$oldData) {
|
|
|
|
let data = {};
|
|
|
|
for (let prop in row.$oldData)
|
|
|
|
data[prop] = row[prop];
|
2018-11-02 14:44:27 +00:00
|
|
|
updates.push({
|
2018-10-19 17:54:46 +00:00
|
|
|
data,
|
|
|
|
where: {[pk]: row.$orgRow[pk]}
|
|
|
|
});
|
2018-10-18 07:24:20 +00:00
|
|
|
}
|
2018-11-29 07:09:40 +00:00
|
|
|
}
|
2018-05-31 09:52:39 +00:00
|
|
|
|
2023-01-05 09:35:42 +00:00
|
|
|
const changes = {deletes, updates, creates};
|
2018-11-02 14:44:27 +00:00
|
|
|
|
2018-11-29 07:09:40 +00:00
|
|
|
for (let prop in changes) {
|
2018-11-02 14:44:27 +00:00
|
|
|
if (changes[prop].length === 0)
|
|
|
|
changes[prop] = undefined;
|
2018-11-29 07:09:40 +00:00
|
|
|
}
|
2018-05-31 09:52:39 +00:00
|
|
|
|
2022-12-29 13:24:32 +00:00
|
|
|
return changes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Saves current changes on the server.
|
|
|
|
*
|
|
|
|
* @return {Promise} The save request promise
|
|
|
|
*/
|
|
|
|
save() {
|
|
|
|
const pk = this.primaryKey;
|
|
|
|
const changes = this.getChanges();
|
|
|
|
if (!changes) return this.$q.resolve();
|
|
|
|
|
2023-01-05 09:35:42 +00:00
|
|
|
const creates = changes.creates || [];
|
2018-10-18 07:24:20 +00:00
|
|
|
let url = this.saveUrl ? this.saveUrl : `${this._url}/crud`;
|
2018-05-31 09:52:39 +00:00
|
|
|
return this.$http.post(url, changes)
|
2020-02-06 06:41:51 +00:00
|
|
|
.then(res => {
|
2020-02-06 09:43:46 +00:00
|
|
|
const created = res.data;
|
2020-02-06 06:41:51 +00:00
|
|
|
|
|
|
|
// Apply new data to created instances
|
2022-12-29 13:24:32 +00:00
|
|
|
for (let i = 0; i < creates.length; i++) {
|
|
|
|
const row = creates[i];
|
2020-02-06 06:41:51 +00:00
|
|
|
row[pk] = created[i][pk];
|
|
|
|
|
|
|
|
for (let prop in row) {
|
|
|
|
if (prop.charAt(0) !== '$')
|
|
|
|
row[prop] = created[i][prop];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-30 14:08:19 +00:00
|
|
|
this.applyChanges();
|
|
|
|
super.save();
|
|
|
|
});
|
2018-05-31 09:52:39 +00:00
|
|
|
}
|
2018-09-05 11:01:21 +00:00
|
|
|
|
|
|
|
buildParams() {
|
|
|
|
let params = {};
|
|
|
|
|
|
|
|
if (this.params instanceof Object)
|
|
|
|
Object.assign(params, this.params);
|
|
|
|
if (this.userParams instanceof Object)
|
|
|
|
Object.assign(params, this.userParams);
|
|
|
|
|
|
|
|
return params;
|
|
|
|
}
|
|
|
|
|
2023-05-11 10:07:14 +00:00
|
|
|
sendRequest(filter, append) {
|
2018-09-05 11:01:21 +00:00
|
|
|
this.cancelRequest();
|
|
|
|
this.canceler = this.$q.defer();
|
2023-05-08 14:50:20 +00:00
|
|
|
this.isPaging = append;
|
2019-10-04 22:23:34 +00:00
|
|
|
|
2023-05-11 10:07:14 +00:00
|
|
|
if (!append && this.status != 'ready')
|
2019-10-04 22:23:34 +00:00
|
|
|
this.status = 'loading';
|
2018-09-05 11:01:21 +00:00
|
|
|
|
|
|
|
let params = Object.assign(
|
2018-10-30 12:58:02 +00:00
|
|
|
{filter},
|
2018-09-05 11:01:21 +00:00
|
|
|
this.buildParams()
|
|
|
|
);
|
|
|
|
let options = {
|
|
|
|
timeout: this.canceler.promise,
|
|
|
|
params: params
|
|
|
|
};
|
|
|
|
|
2018-10-18 07:24:20 +00:00
|
|
|
return this.$http.get(this._url, options).then(
|
2023-05-08 14:50:20 +00:00
|
|
|
json => this.onRemoteDone(json, filter, append),
|
|
|
|
json => this.onRemoteError(json, append)
|
2019-05-10 12:42:46 +00:00
|
|
|
).finally(() => {
|
|
|
|
this.isPaging = false;
|
|
|
|
});
|
2018-09-05 11:01:21 +00:00
|
|
|
}
|
|
|
|
|
2023-05-08 14:50:20 +00:00
|
|
|
onRemoteDone(json, filter, append) {
|
2018-09-05 11:01:21 +00:00
|
|
|
let data = json.data;
|
2023-05-08 14:50:20 +00:00
|
|
|
if (append)
|
2018-09-05 11:01:21 +00:00
|
|
|
this.orgData = this.orgData.concat(data);
|
2018-10-30 14:08:19 +00:00
|
|
|
else {
|
2018-09-05 11:01:21 +00:00
|
|
|
this.orgData = data;
|
|
|
|
this.currentFilter = filter;
|
|
|
|
}
|
2018-10-18 07:24:20 +00:00
|
|
|
this.data = this.proxiedData.slice();
|
2018-09-05 11:01:21 +00:00
|
|
|
this.moreRows = filter.limit && data.length == filter.limit;
|
|
|
|
this.onRequestEnd();
|
|
|
|
}
|
|
|
|
|
2023-05-08 14:50:20 +00:00
|
|
|
onRemoteError(err, append) {
|
|
|
|
if (!append) {
|
2019-10-03 17:34:24 +00:00
|
|
|
this.clear();
|
|
|
|
this.status = 'error';
|
|
|
|
}
|
|
|
|
|
2018-09-05 11:01:21 +00:00
|
|
|
this.onRequestEnd();
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
|
|
|
|
onRequestEnd() {
|
|
|
|
this.canceler = null;
|
|
|
|
}
|
2018-10-18 07:24:20 +00:00
|
|
|
|
|
|
|
undoChanges() {
|
|
|
|
super.undoChanges();
|
|
|
|
this.data = this.proxiedData.slice();
|
|
|
|
}
|
2018-05-31 09:52:39 +00:00
|
|
|
}
|
2018-10-22 06:23:10 +00:00
|
|
|
CrudModel.$inject = ['$q', '$http', '$element', '$scope'];
|
2018-05-31 09:52:39 +00:00
|
|
|
|
2020-07-24 15:46:06 +00:00
|
|
|
ngModule.vnComponent('vnCrudModel', {
|
2018-05-31 09:52:39 +00:00
|
|
|
controller: CrudModel,
|
|
|
|
bindings: {
|
|
|
|
orgData: '<?',
|
|
|
|
data: '=?',
|
2018-07-02 09:52:50 +00:00
|
|
|
onDataChange: '&?',
|
2018-05-31 09:52:39 +00:00
|
|
|
link: '<?',
|
|
|
|
url: '@?',
|
|
|
|
saveUrl: '@?',
|
2018-10-18 07:24:20 +00:00
|
|
|
fields: '<?',
|
2018-05-31 09:52:39 +00:00
|
|
|
include: '<?',
|
2018-10-18 07:24:20 +00:00
|
|
|
where: '<?',
|
2018-05-31 09:52:39 +00:00
|
|
|
order: '@?',
|
|
|
|
limit: '<?',
|
|
|
|
filter: '<?',
|
2018-09-05 11:01:21 +00:00
|
|
|
params: '<?',
|
|
|
|
userFilter: '<?',
|
2018-07-09 16:06:25 +00:00
|
|
|
userParams: '<?',
|
2018-05-31 09:52:39 +00:00
|
|
|
primaryKey: '@?',
|
2018-10-30 14:08:19 +00:00
|
|
|
autoLoad: '<?',
|
|
|
|
autoSave: '<?'
|
2018-05-31 09:52:39 +00:00
|
|
|
}
|
|
|
|
});
|