260 lines
6.7 KiB
JavaScript
260 lines
6.7 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(changes) {
|
|
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 {
|
|
this.check();
|
|
} 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();
|
|
|
|
if (!this.dataChanged()) {
|
|
this.updateOriginalData();
|
|
return this.$q.resolve();
|
|
}
|
|
|
|
let isPost = (this.$attrs.save && this.$attrs.save.toLowerCase() === 'post');
|
|
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
|
|
);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 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');
|
|
}
|
|
|
|
/**
|
|
* Notifies the user that the data has been saved.
|
|
*/
|
|
notifySaved() {
|
|
this.vnApp.showSuccess(this._.instant('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();
|
|
}
|
|
|
|
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
|
|
});
|