-
Rafael Ostertag authoredRafael Ostertag authored
QfqForm.js 15.27 KiB
/**
* @author Rafael Ostertag <rafael.ostertag@math.uzh.ch>
*/
/* global $ */
if (!QfqNS) {
var QfqNS = {};
}
(function (n) {
'use strict';
n.QfqForm = function (formId, submitTo, deleteUrl, dataRefreshUrl) {
this.formId = formId;
this.submitTo = submitTo;
this.deleteUrl = deleteUrl;
this.dataRefreshUrl = dataRefreshUrl;
this.form = new n.Form(this.formId);
this.bsTabs = null;
this.lastButtonPress = null;
this.destroyFormUserCallbacks = new n.Helper.FunctionList();
this.getSaveButton().addClass("disabled").attr("disabled", "disabled");
this.form.addChangeHandler(this.changeHandler.bind(this));
this.form.addResetHandler(this.resetHandler.bind(this));
this.form.addSubmitSuccessHandler(this.submitSuccessDispatcher.bind(this));
this.form.addSubmitFailureHandler(this.submitFailureHandler.bind(this));
this.getSaveButton().click(this.handleSaveClick.bind(this));
this.getCloseButton().click(this.handleCloseClick.bind(this));
this.getNewButton().click(this.handleNewClick.bind(this));
this.getDeleteButton().click(this.handleDeleteClick.bind(this));
this.setupFormUpdateHandler();
};
n.QfqForm.prototype.setBsTabs = function (bsTabs) {
this.bsTabs = bsTabs;
};
/**
*
* @param $button
* @param enabled {boolean}
*
* @private
*/
n.QfqForm.prototype.setButtonEnabled = function ($button, enabled) {
if (!$button) {
n.Log.error("QfqForm#setButtonEnabled(): no button provided.");
return;
}
if (!enabled) {
$button.addClass("disabled");
$button.attr("disabled", "disabled");
} else {
$button.removeClass("disabled");
$button.removeAttr("disabled");
}
};
n.QfqForm.prototype.setupFormUpdateHandler = function () {
$('input[data-load],select[data-load]').on('change', this.formUpdateHandler.bind(this));
};
n.QfqForm.prototype.formUpdateHandler = function () {
var that = this;
$.post(this.dataRefreshUrl, this.form.serialize(), "json")
.fail(function (jqXHR, textStatus, errorThrown) {
var alert = new QfqNS.Alert("Error:<br>" + errorThrown, "error");
alert.show();
})
.done(function (data) {
this.handleFormUpdate(data);
}.bind(that));
};
n.QfqForm.prototype.handleFormUpdate = function (data) {
if (!data.status) {
throw new Error("Expected 'status' attribute to be present.");
}
if (data.status === "error") {
var alert = new QfqNS.Alert("Error while updating form:<br>" + (data.message ? data.message : "No reason" +
" given"), "error");
alert.show();
return;
}
if (data.status === "success") {
if (!data['form-update']) {
throw new Error("'form-update' attribute missing in form update data");
}
this.applyElementConfiguration(data['form-update']);
return;
}
throw new Error("Unexpected status: '" + data.status + "'");
};
/**
* @private
*/
n.QfqForm.prototype.destroyFormAndSetText = function (text) {
this.form = null;
$('#' + this.formId).replaceWith($("<p>").append(text));
this.destroyFormUserCallbacks.call();
};
/**
* @private
*/
n.QfqForm.prototype.handleSaveClick = function () {
this.lastButtonPress = "save";
QfqNS.Log.debug("save click");
// First, remove all validation states, in case a previous submit has set a validation state, thus we're not
// stockpiling them.
this.clearAllValidationStates();
this.form.submitTo(this.submitTo);
};
/**
* @private
*/
n.QfqForm.prototype.handleCloseClick = function () {
this.lastButtonPress = "close";
if (this.form.getFormChanged()) {
var alert = new QfqNS.Alert("You have unsaved changes. Do you want to close?", "warning", "yesnosave");
var that = this;
alert.addSaveButtonHandler(function () {
that.form.submitTo(that.submitTo);
});
alert.addOkButtonHandler(function () {
window.history.back();
});
alert.show();
} else {
window.history.back();
}
};
/**
* @private
*/
n.QfqForm.prototype.handleNewClick = function () {
this.lastButtonPress = "new";
QfqNS.Log.debug("new click");
};
/**
* @private
*/
n.QfqForm.prototype.handleDeleteClick = function () {
this.lastButtonPress = "delete";
QfqNS.Log.debug("delete click");
var alert = new QfqNS.Alert("Do you really want to delete the record?", "warning", "yesno");
var that = this;
alert.addOkButtonHandler(function () {
$.post(that.deleteUrl)
.done(that.ajaxDeleteSuccessDispatcher.bind(that))
.fail(that.ajaxDeleteFailureHandler.bind(that));
});
alert.show();
};
/**
*
* @param data
* @param textStatus
* @param jqXHR
*
* @private
*/
n.QfqForm.prototype.ajaxDeleteSuccessDispatcher = function (data, textStatus, jqXHR) {
if (!data.status) {
throw new Error("No 'status' property 'data'");
}
switch (data.status) {
case "error":
this.handleLogicDeleteError(data);
break;
case "success":
this.handleDeleteSuccess(data);
break;
default:
throw new Error("Status '" + data.status + "' unknown.");
}
};
/**
*
* @param data
*
* @private
*/
n.QfqForm.prototype.handleDeleteSuccess = function (data) {
this.setButtonEnabled(this.getCloseButton(), false);
this.setButtonEnabled(this.getDeleteButton(), false);
this.setButtonEnabled(this.getSaveButton(), false);
this.setButtonEnabled(this.getNewButton(), false);
this.destroyFormAndSetText("Record has been deleted!");
if (!data.redirect || data.redirect === "client") {
window.history.back();
return;
}
if (data.redirect === "no") {
var alert = new QfqNS.Alert("redirect=='no' not allowed", "error");
alert.show();
return;
}
if (data.redirect === "url" || data['redirect-url']) {
window.location = data['redirect-url'];
return;
}
};
/**
*
* @param data
*
* @private
*/
n.QfqForm.prototype.handleLogicDeleteError = function (data) {
if (!data.message) {
throw Error("Status is 'error' but required 'message' attribute is missing.");
}
var alert = new QfqNS.Alert(data.message, "error");
alert.show();
};
/**
*
* @param form {QfqNS.QfqForm}
*
* @private
*/
n.QfqForm.prototype.changeHandler = function (form) {
this.getSaveButton().removeClass("disabled");
this.getSaveButton().addClass("alert-warning");
this.getSaveButton().removeAttr("disabled");
};
/**
*
* @param form {QfqNS.QfqForm}
*
* @private
*/
n.QfqForm.prototype.resetHandler = function (form) {
this.getSaveButton().removeClass("alert-warning");
this.getSaveButton().addClass("disabled");
this.getSaveButton().attr("disabled", "disabled");
};
/**
*
* @returns {jQuery|HTMLElement}
*
* @private
*/
n.QfqForm.prototype.getSaveButton = function () {
return $("#save-button");
};
/**
*
* @returns {jQuery|HTMLElement}
*
* @private
*/
n.QfqForm.prototype.getCloseButton = function () {
return $("#close-button");
};
/**
*
* @returns {jQuery|HTMLElement}
*
* @private
*/
n.QfqForm.prototype.getDeleteButton = function () {
return $("#delete-button");
};
/**
*
* @returns {jQuery|HTMLElement}
*
* @private
*/
n.QfqForm.prototype.getNewButton = function () {
return $("#new-button");
};
/**
*
* @param jqXHR
* @param textStatus
* @param errorThrown
*
* @private
*/
n.QfqForm.prototype.submitFailureHandler = function (form, textStatus, jqXHR, errorThrown) {
this.showAjaxError(errorThrown);
};
/**
*
* @param jqXHR
* @param textStatus
* @param errorThrown
*
* @private
*/
n.QfqForm.prototype.ajaxDeleteFailureHandler = function (jqXHR, textStatus, errorThrown) {
this.showAjaxError(errorThrown);
};
n.QfqForm.prototype.showAjaxError = function (errorThrown) {
var alert = new QfqNS.Alert("Error:<br> " +
errorThrown, "error");
alert.show();
};
/**
* @private
*/
n.QfqForm.prototype.submitSuccessDispatcher = function (form, data, textStatus) {
if (!data.status) {
throw new Error("No 'status' property in 'data'");
}
switch (data.status) {
case "error":
this.handleLogicSubmitError(form, data);
break;
case "success":
this.handleSubmitSuccess(form, data);
break;
default:
throw new Error("Status '" + data.status + "' unknown.");
}
};
/**
*
* @param form
* @param data
*
* @private
*/
n.QfqForm.prototype.handleLogicSubmitError = function (form, data) {
if (!data.message) {
throw Error("Status is 'error' but required 'message' attribute is missing.");
}
var alert = new QfqNS.Alert(data.message, "error");
alert.show();
if (data["field-name"] && this.bsTabs) {
var tabId = this.bsTabs.getContainingTabIdForFormControl(data["field-name"]);
if (tabId) {
this.bsTabs.activateTab(tabId);
}
this.setValidationState(data["field-name"], "error");
this.setHelpBlockValidationMessage(data["field-name"], data["field-message"]);
}
};
/**
*
* @param form
* @param data
*
* @private
*/
n.QfqForm.prototype.handleSubmitSuccess = function (form, data) {
QfqNS.Log.debug('Reset form state');
form.resetFormChanged();
if (this.lastButtonPress === 'save' || !data.redirect || data.redirect === "no") {
if (data.message) {
var alert = new QfqNS.Alert(data.message);
alert.timeout = 1500;
alert.show();
}
// do we have to update the HTML Form?
if (data['form-update']) {
this.applyElementConfiguration(data['form-update']);
}
return;
}
if (data.redirect === "client") {
window.history.back();
return;
}
if (data.redirect === "url" || data['redirect-url']) {
window.location = data['redirect-url'];
return;
}
};
n.QfqForm.prototype.getFormGroupByControlName = function (formControlName) {
var $formControl = $("[name='" + formControlName + "']");
if ($formControl.length === 0) {
QfqNS.Log.debug("QfqForm.setValidationState(): unable to find form control with name '" + formControlName + "'");
return null;
}
var iterator = $formControl[0];
while (iterator !== null) {
var $iterator = $(iterator);
if ($iterator.hasClass('form-group')) {
return $iterator;
}
iterator = iterator.parentElement;
}
return null;
};
n.QfqForm.prototype.setValidationState = function (formControlName, state) {
var $formGroup = this.getFormGroupByControlName(formControlName);
if ($formGroup) {
$formGroup.addClass("has-" + state);
}
};
n.QfqForm.prototype.resetValidationState = function (formControlName) {
var $formGroup = this.getFormGroupByControlName(formControlName);
$formGroup.removeClass("has-warning");
$formGroup.removeClass("has-error");
$formGroup.removeClass("has-success");
};
n.QfqForm.prototype.clearAllValidationStates = function () {
$('.has-warning,.has-error,.has-success').removeClass("has-warning has-error has-success");
$('[data-qfq=validation-message]').remove();
};
/**
*
* @param formControlName
* @param text
*/
n.QfqForm.prototype.setHelpBlockValidationMessage = function (formControlName, text) {
/*
* Why is this method here and not in FormGroup? Adding this particular method to FormGroup is easy, however
* QfqForm.clearAllValidationStates() would not find its proper place in FormGroup, since FormGroup operates
* on one element. We would end up having the responsibilities spread across several classes, which would be
* confusing.
*/
var $formGroup = this.getFormGroupByControlName(formControlName);
if (!$formGroup) {
return;
}
var $helpBlockColumn;
var $formGroupSubDivs = $formGroup.find("div");
if ($formGroupSubDivs.length < 3) {
$helpBlockColumn = $("<div>").addClass("col-md-4");
$formGroup.append($helpBlockColumn);
} else {
$helpBlockColumn = $($formGroupSubDivs[2]);
}
$helpBlockColumn.append(
$("<p>")
.addClass("help-block")
.attr("data-qfq", "validation-message")
.append(text)
);
};
/**
*
* @param configuration {array} array of objects.
*/
n.QfqForm.prototype.applyElementConfiguration = function (configuration) {
var arrayLength = configuration.length;
for (var i = 0; i < arrayLength; i++) {
var configurationItem = configuration[i];
var formElementName = configurationItem["form-element"];
if (formElementName === undefined) {
QfqNS.Log.error("configuration lacks 'form-element' attribute. Skipping.");
continue;
}
try {
var element = n.Element.getElement(formElementName);
if (configurationItem.value !== undefined) {
element.setValue(configurationItem.value);
}
if (configurationItem.readonly !== undefined) {
element.setReadOnly(configurationItem.readonly);
}
if (configurationItem.disabled !== undefined) {
element.setEnabled(!configurationItem.disabled);
}
} catch (e) {
QfqNS.Log.error(e.message);
}
}
};
/**
* Retrieve SIP as stored in hidden input field.
*
* @returns {string} sip
*/
n.QfqForm.prototype.getSip = function () {
return $('#' + this.formId + ' input[name=s]').val();
};
})(QfqNS);