salix/front/core/components/watcher/watcher.js

282 lines
7.2 KiB
JavaScript

import ngModule from '../../module';
import Component from '../../lib/component';
import getModifiedData from '../../lib/modified';
import copyObject from '../../lib/copy';
import isEqual from '../../lib/equals';
import isFullEmpty from '../../lib/full-empty';
import UserError from '../../lib/user-error';
/**
* Component that checks for changes on a specific model property and
* asks the user to save or discard it when the state changes.
* Also it can save the data to the server when the @url and @idField
* properties are provided.
*/
export default class Watcher extends Component {
constructor($element, $, $state, $stateParams, $transitions, $http, vnApp, $translate, $attrs, $q) {
super($element);
Object.assign(this, {
$,
$state,
$stateParams,
$http,
_: $translate,
$attrs,
vnApp,
$q
});
this.state = null;
this.deregisterCallback = $transitions.onStart({},
transition => this.callback(transition));
this.updateOriginalData();
}
$onInit() {
if (this.get && this.url)
this.fetchData();
else if (this.get && !this.url)
throw new Error('URL parameter ommitted');
}
$onChanges() {
if (this.data)
this.updateOriginalData();
}
$onDestroy() {
this.deregisterCallback();
}
get dirty() {
return this.form && this.form.$dirty || this.dataChanged();
}
dataChanged() {
let data = this.copyInNewObject(this.data);
return !isEqual(data, this.orgData);
}
fetchData() {
let id = this.data[this.idField];
return this.$http.get(`${this.url}/${id}`).then(
json => {
this.data = copyObject(json.data);
this.updateOriginalData();
}
);
}
/**
* Submits the data and goes back in the history.
*
* @return {Promise} The request promise
*/
submitBack() {
return this.submit().then(res => {
this.window.history.back();
return res;
});
}
/**
* Submits the data and goes another state.
*
* @param {String} state The state name
* @param {Object} params The request params
* @return {Promise} The request promise
*/
submitGo(state, params) {
return this.submit().then(res => {
this.$state.go(state, params || {});
return res;
});
}
/**
* Submits the data to the server.
*
* @return {Promise} The http request promise
*/
submit() {
try {
if (this.requestMethod() !== 'post')
this.check();
else this.isInvalid();
} catch (err) {
return this.$q.reject(err);
}
return this.realSubmit().then(res => {
this.notifySaved();
return res;
});
}
/**
* Submits the data without checking data validity or changes.
*
* @return {Promise} The http request promise
*/
realSubmit() {
if (this.form)
this.form.$setSubmitted();
const isPost = (this.requestMethod() === 'post');
if (!this.dataChanged() && !isPost) {
this.updateOriginalData();
return this.$q.resolve();
}
let changedData = isPost
? this.data
: getModifiedData(this.data, this.orgData);
let id = this.idField ? this.orgData[this.idField] : null;
// If watcher is associated to mgCrud
if (this.save && this.save.accept) {
if (id)
changedData[this.idField] = id;
this.save.model = changedData;
return this.$q((resolve, reject) => {
this.save.accept().then(
json => this.writeData({data: json}, resolve),
reject
);
});
}
// When mgCrud is not used
if (id) {
return this.$q((resolve, reject) => {
this.$http.patch(`${this.url}/${id}`, changedData).then(
json => this.writeData(json, resolve),
reject
);
});
}
return this.$q((resolve, reject) => {
this.$http.post(this.url, changedData).then(
json => this.writeData(json, resolve),
reject
);
});
}
/**
* return the request method.
*/
requestMethod() {
return this.$attrs.save && this.$attrs.save.toLowerCase();
}
/**
* Checks if data is ready to send.
*/
check() {
if (this.form && this.form.$invalid)
throw new UserError('Some fields are invalid');
if (!this.dirty)
throw new UserError('No changes to save');
}
/**
* Checks if the form is valid.
*/
isInvalid() {
if (this.form && this.form.$invalid)
throw new UserError('Some fields are invalid');
}
/**
* Notifies the user that the data has been saved.
*/
notifySaved() {
this.vnApp.showSuccess(this.$t('Data saved!'));
}
setPristine() {
if (this.form) this.form.$setPristine();
}
setDirty() {
if (this.form) this.form.$setDirty();
}
callback(transition) {
if (!this.state && this.dirty) {
this.state = transition.to().name;
this.$.confirm.show();
return false;
}
return true;
}
onConfirmResponse(response) {
if (response === 'accept') {
if (this.data)
Object.assign(this.data, this.orgData);
this.$state.go(this.state);
} else
this.state = null;
}
writeData(json, resolve) {
Object.assign(this.data, json.data);
this.updateOriginalData();
resolve(json);
}
updateOriginalData() {
this.orgData = this.copyInNewObject(this.data);
this.setPristine();
}
loadOriginalData() {
const orgData = JSON.parse(JSON.stringify(this.orgData));
this.data = Object.assign(this.data, orgData);
this.setPristine();
}
copyInNewObject(data) {
let newCopy = {};
if (data && typeof data === 'object') {
Object.keys(data).forEach(
key => {
let value = data[key];
if (value instanceof Date)
newCopy[key] = new Date(value.getTime());
else if (!isFullEmpty(value)) {
if (typeof value === 'object')
newCopy[key] = this.copyInNewObject(value);
else
newCopy[key] = value;
}
}
);
}
return newCopy;
}
}
Watcher.$inject = ['$element', '$scope', '$state', '$stateParams', '$transitions', '$http', 'vnApp', '$translate', '$attrs', '$q'];
ngModule.component('vnWatcher', {
template: require('./watcher.html'),
bindings: {
url: '@?',
idField: '@?',
data: '<',
form: '<',
save: '<',
get: '<?'
},
controller: Watcher
});