545 lines
15 KiB
JavaScript
545 lines
15 KiB
JavaScript
/* Xeslint-disable */
|
|
///////////////////////////////
|
|
// gzform
|
|
//
|
|
// provides form services and utilities
|
|
// validation services
|
|
// dirty and change tracking
|
|
// and also general error display in forms
|
|
//probably should be broken up more
|
|
// All locale keys for validation *MUST* be fetched prior to this being used as it assumes all keys are fetched first
|
|
// Add any new keys used to the block in locale.js=>commonKeysEditForm
|
|
import Vue from "vue";
|
|
|
|
var triggeringChange = false;
|
|
|
|
function isEmpty(o) {
|
|
if (typeof o == "number" && o == 0) {
|
|
return false;
|
|
}
|
|
return !o;
|
|
}
|
|
|
|
////////////////////////////////////
|
|
// isInt value??
|
|
//
|
|
//FROM HERE: https://stackoverflow.com/a/14794066/8939
|
|
//fast test if is an integer:
|
|
function isInt(value) {
|
|
var x;
|
|
if (isNaN(value)) {
|
|
return false;
|
|
}
|
|
x = parseFloat(value);
|
|
return (x | 0) === x;
|
|
}
|
|
|
|
////////////////////////////////////
|
|
// isNumber
|
|
//
|
|
//FROM HERE: https://stackoverflow.com/a/1830632/8939
|
|
function isNumber(n) {
|
|
return !isNaN(parseFloat(n)) && isFinite(n);
|
|
}
|
|
|
|
////////////////////////////////////
|
|
// Get control from ref
|
|
//
|
|
function getControl(vm, ref) {
|
|
var ctrl = vm.$refs[ref];
|
|
|
|
return ctrl;
|
|
}
|
|
|
|
////////////////////////////////////
|
|
// Get value from control
|
|
//
|
|
function getControlValue(ctrl) {
|
|
var value = ctrl.value;
|
|
return value;
|
|
}
|
|
|
|
////////////////////////////////////
|
|
// Get field name from control
|
|
//
|
|
function getControlLabel(ctrl) {
|
|
if (window.$gz.errorHandler.developmentModeShowErrorsImmediately) {
|
|
if (!ctrl.label) {
|
|
throw "gzform:getControlLabel - the control has no label " + ctrl;
|
|
}
|
|
}
|
|
return ctrl.label;
|
|
}
|
|
|
|
/////////////////////////////////////////
|
|
// Get errors for a particular field
|
|
// from server error collection
|
|
//
|
|
function getErrorsForField(vm, ref) {
|
|
var ret = [];
|
|
if (ref == "errorbox") {
|
|
ret = window.$gz._.filter(vm.formState.serverError.details, function(o) {
|
|
return !o.target;
|
|
});
|
|
} else {
|
|
ret = window.$gz._.filter(vm.formState.serverError.details, function(o) {
|
|
if (!o.target) {
|
|
return false;
|
|
}
|
|
//server error fields are capitalized
|
|
return o.target.toLowerCase() == ref;
|
|
});
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
///////////////////////////////
|
|
// ERROR BOX ERRORS
|
|
// gathers any messages for error box on form which is the generic catch all for non field specific errors from server
|
|
// and application itself locally
|
|
function getErrorBoxErrors(vm, errs) {
|
|
var hasErrors = false;
|
|
var ret = "";
|
|
if (errs.length > 0) {
|
|
hasErrors = true;
|
|
//loop array and append each error to a return string
|
|
for (var i = 0; i < errs.length; i++) {
|
|
ret += errs[i] + "\r\n";
|
|
}
|
|
}
|
|
|
|
//any application errors?
|
|
if (vm.formState.appError) {
|
|
hasErrors = true;
|
|
ret = vm.formState.appError + "\r\n----------\r\n" + ret;
|
|
}
|
|
|
|
if (!hasErrors) {
|
|
return null;
|
|
} else {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
export default {
|
|
///////////////////////////////
|
|
// REQUIRED
|
|
//
|
|
required(vm, ref) {
|
|
if (vm.formState.loading) {
|
|
return false;
|
|
}
|
|
var ctrl = getControl(vm, ref);
|
|
if (typeof ctrl == "undefined") {
|
|
return false;
|
|
}
|
|
|
|
var value = getControlValue(ctrl);
|
|
if (!isEmpty(value)) {
|
|
return false;
|
|
}
|
|
|
|
// "ErrorRequiredFieldEmpty": "{0} is a required field. Please enter a value for {0}",
|
|
var err = window.$gz.locale.get("ErrorRequiredFieldEmpty");
|
|
var fieldName = getControlLabel(ctrl);
|
|
err = window.$gz._.replace(err, "{0}", fieldName);
|
|
//lodash replace only replaces first instance so need to do it twice
|
|
err = window.$gz._.replace(err, "{0}", fieldName);
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: false
|
|
});
|
|
return err;
|
|
},
|
|
///////////////////////////////
|
|
// MAXLENGTH
|
|
//
|
|
maxLength(vm, ref, max) {
|
|
if (vm.formState.loading) {
|
|
return false;
|
|
}
|
|
var ctrl = getControl(vm, ref);
|
|
if (typeof ctrl == "undefined") {
|
|
return false;
|
|
}
|
|
|
|
var value = getControlValue(ctrl);
|
|
if (isEmpty(value)) {
|
|
return false;
|
|
}
|
|
|
|
if (value.length > max) {
|
|
//get the localized rule text
|
|
// "ErrorFieldLengthExceeded": "{0} can not exceed {1} characters.",
|
|
var err = window.$gz.locale.get("ErrorFieldLengthExceeded");
|
|
var fieldName = getControlLabel(ctrl);
|
|
err = window.$gz._.replace(err, "{0}", fieldName);
|
|
err = window.$gz._.replace(err, "{1}", max);
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: false
|
|
});
|
|
return err;
|
|
} else {
|
|
return false;
|
|
}
|
|
},
|
|
///////////////////////////////
|
|
// MAX 255
|
|
//
|
|
max255(vm, ref) {
|
|
if (vm.formState.loading) {
|
|
return false;
|
|
}
|
|
return this.maxLength(vm, ref, 255);
|
|
},
|
|
///////////////////////////////
|
|
// DatePrecedence
|
|
// (start date must precede end date)
|
|
//
|
|
datePrecedence(vm, refStart, refEnd) {
|
|
if (vm.formState.loading) {
|
|
return false;
|
|
}
|
|
var ctrlStart = getControl(vm, refStart);
|
|
if (typeof ctrlStart == "undefined") {
|
|
return false;
|
|
}
|
|
|
|
var ctrlEnd = getControl(vm, refEnd);
|
|
if (typeof ctrlEnd == "undefined") {
|
|
return false;
|
|
}
|
|
|
|
var valueStart = getControlValue(ctrlStart);
|
|
if (isEmpty(valueStart)) {
|
|
return false;
|
|
}
|
|
|
|
var valueEnd = getControlValue(ctrlEnd);
|
|
if (isEmpty(valueEnd)) {
|
|
return false;
|
|
}
|
|
|
|
valueStart = window.$gz.dayjs(valueStart);
|
|
valueEnd = window.$gz.dayjs(valueEnd);
|
|
|
|
// if either is not valid.
|
|
if (!valueStart || !valueEnd) {
|
|
return false;
|
|
}
|
|
|
|
if (valueStart.isAfter(valueEnd)) {
|
|
// "ErrorStartDateAfterEndDate": "Start date must be earlier than stop / end date",
|
|
var err = window.$gz.locale.get("ErrorStartDateAfterEndDate");
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: false
|
|
});
|
|
return err;
|
|
} else {
|
|
return false;
|
|
}
|
|
},
|
|
///////////////////////////////
|
|
// INTEGER IS VALID
|
|
//
|
|
integerValid(vm, ref) {
|
|
if (vm.formState.loading) {
|
|
return false;
|
|
}
|
|
var ctrl = getControl(vm, ref);
|
|
if (typeof ctrl == "undefined") {
|
|
return false;
|
|
}
|
|
|
|
//DEBUG
|
|
//logControl("integerValid", ctrl, ref);
|
|
|
|
var value = getControlValue(ctrl);
|
|
if (isEmpty(value)) {
|
|
return false;
|
|
}
|
|
|
|
if (isInt(value)) {
|
|
return false;
|
|
}
|
|
|
|
// "ErrorFieldValueNotInteger": "Value must be an integer"
|
|
var err = window.$gz.locale.get("ErrorFieldValueNotInteger");
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: false
|
|
});
|
|
return err;
|
|
},
|
|
///////////////////////////////
|
|
// DECIMAL
|
|
// Basically anything that can be a number is valid
|
|
//
|
|
decimalValid(vm, ref) {
|
|
if (vm.formState.loading) {
|
|
return false;
|
|
}
|
|
//TODO: Handle commas and spaces in numbers
|
|
//as per window.$gz.locale rules for numbers
|
|
|
|
var ctrl = getControl(vm, ref);
|
|
if (typeof ctrl == "undefined") {
|
|
return false;
|
|
}
|
|
|
|
//DEBUG
|
|
//logControl("decimalValid", ctrl, ref);
|
|
|
|
var value = getControlValue(ctrl);
|
|
if (isEmpty(value)) {
|
|
return false;
|
|
}
|
|
|
|
if (isNumber(value)) {
|
|
return false;
|
|
}
|
|
|
|
// "ErrorFieldValueNotDecimal": "Value must be a number"
|
|
var err = window.$gz.locale.get("ErrorFieldValueNotDecimal");
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: false
|
|
});
|
|
return err;
|
|
},
|
|
///////////////////////////////
|
|
// SERVER ERRORS
|
|
// Process and return server errors if any for form and field specified
|
|
//
|
|
serverErrors(vm, ref) {
|
|
//CHECK PREREQUISITES IN DEV MODE TO ENSURE FORM ISN"T MISSING NEEDED DATA ATTRIBUTES ETC
|
|
if (window.$gz.window.$gz.errorHandler.devMode()) {
|
|
//make sure formState.serverErrors is defined on data
|
|
if (!window.$gz._.has(vm, "formState.serverError")) {
|
|
throw "DEV ERROR gzform::formState.serverErrors -> formState.serverError seems to be missing from form's vue data object";
|
|
}
|
|
|
|
//make sure formState.appError is defined on data
|
|
if (!window.$gz._.has(vm, "formState.appError")) {
|
|
throw "DEV ERROR gzform::formState.serverErrors -> formState.appError seems to be missing from form's vue data object";
|
|
}
|
|
|
|
//make sure formState.errorBoxMessage is defined on data
|
|
if (!window.$gz._.has(vm, "formState.errorBoxMessage")) {
|
|
throw "DEV ERROR gzform::formState.serverErrors -> formState.errorBoxMessage seems to be missing from form's vue data object";
|
|
}
|
|
|
|
//ensure the error returned is in an expected format to catch coding errors at the server end
|
|
if (!window.$gz._.isEmpty(vm.formState.serverError)) {
|
|
//Make sure there is an error code if there is an error collection
|
|
if (!vm.formState.serverError.code) {
|
|
throw "DEV ERROR gzform::formState.serverErrors -> server returned error without code";
|
|
}
|
|
}
|
|
}
|
|
var ret = [];
|
|
|
|
//check for errors if we have any errors
|
|
if (!window.$gz._.isEmpty(vm.formState.serverError)) {
|
|
//debugger;
|
|
//First let's get the top level error code
|
|
|
|
var apiErrorCode = parseInt(vm.formState.serverError.code);
|
|
|
|
//GENERAL ERROR
|
|
if (ref == "errorbox") {
|
|
//Add any general errors to ret
|
|
var err = window.$gz.locale.get("ErrorAPI" + apiErrorCode.toString());
|
|
if (vm.formState.serverError.message) {
|
|
err = err + "\r\n" + vm.formState.serverError.message;
|
|
}
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: false
|
|
});
|
|
ret.push(err);
|
|
}
|
|
//DETAIL ERRORS
|
|
//{"error":{"code":"2200","details":[{"message":"Exception: Error converting value \"\" to type 'AyaNova.Biz.AUTHORIZATION_ROLES'. Path 'roles', line 1, position 141.","target":"roles","error":"2203"}],"message":"Object did not pass validation"}}
|
|
//Specific field validation errors are in an array in "details" key
|
|
if (!window.$gz._.isEmpty(vm.formState.serverError.details)) {
|
|
//See if this key is in the details array
|
|
var errorsForField = getErrorsForField(vm, ref);
|
|
|
|
if (errorsForField.length > 0) {
|
|
//iterate the errorsForField object and add each to return array of errors
|
|
window.$gz._.each(errorsForField, function(ve) {
|
|
var fldErr = "";
|
|
var fldErrorCode = parseInt(ve.error);
|
|
fldErr =
|
|
window.$gz.locale.get("ErrorAPI" + fldErrorCode.toString()) +
|
|
" [" +
|
|
ve.error +
|
|
"]";
|
|
if (ve.message) {
|
|
fldErr += ' - "' + ve.message + '"';
|
|
}
|
|
ret.push(fldErr);
|
|
});
|
|
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: false
|
|
});
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
//default if no error message to display
|
|
return ret;
|
|
},
|
|
|
|
///////////////////////////////
|
|
// ClearformState.serverErrors
|
|
// Clear all server errors and app errors and ensure error box doesn't show
|
|
//
|
|
deleteAllErrorBoxErrors(vm) {
|
|
//clear all keys from server error
|
|
window.$gz.util.removeAllPropertiesFromObject(vm.formState.serverError);
|
|
//clear app errors
|
|
vm.formState.appError = null;
|
|
//clear out actual message box display
|
|
vm.formState.errorBoxMessage = null;
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: true
|
|
});
|
|
},
|
|
///////////////////////////////
|
|
// setErrorBoxErrors
|
|
// Gather server errors and set the appropriate keys
|
|
//
|
|
setErrorBoxErrors(vm) {
|
|
var errs = this.serverErrors(vm, "errorbox");
|
|
var ret = getErrorBoxErrors(vm, errs);
|
|
vm.formState.errorBoxMessage = ret;
|
|
},
|
|
///////////////////////////////
|
|
// On onChange handler
|
|
// This is required so that server errors can be cleared when input is changed
|
|
//
|
|
onChange(vm, ref) {
|
|
//xeslint-disable-next-line
|
|
//console.log("GZFORM::onChange triggered!");
|
|
if (triggeringChange || vm.formState.loading) {
|
|
return;
|
|
}
|
|
//If ref appears in the formState.serverErrors details collection, remove each one
|
|
var m = window.$gz._.remove(vm.formState.serverError.details, function(o) {
|
|
if (!o.target) {
|
|
return false;
|
|
}
|
|
return o.target.toLowerCase() == ref;
|
|
});
|
|
|
|
//If there are no more errors in details then remove the whole thing as it's no longer required
|
|
if (
|
|
vm.formState.serverError.details &&
|
|
vm.formState.serverError.details.length < 1
|
|
) {
|
|
if (vm.formState.serverError.code == "2200") {
|
|
//clear all keys from server error
|
|
window.$gz.util.removeAllPropertiesFromObject(vm.formState.serverError);
|
|
}
|
|
}
|
|
|
|
//Clear out old validation display in form by forcing the control's data to change
|
|
//I tried calling form validate and reset and all that bullshit but it did nothing
|
|
//probably because it has safeguards to prevent excess validation, this works though so far
|
|
//I added the triggering change guard but it actually doesn't seem to be required here, more investigation is required
|
|
if (m.length > 0) {
|
|
triggeringChange = true;
|
|
var val = vm.obj[ref];
|
|
vm.obj[ref] = null;
|
|
vm.obj[ref] = val;
|
|
triggeringChange = false;
|
|
}
|
|
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
dirty: true,
|
|
valid: vm.$refs.form.validate()
|
|
});
|
|
},
|
|
////////////////////////////////////
|
|
// set calling form Valid state
|
|
//
|
|
// {vm:vm,dirty:bool | undefined,
|
|
// valid:bool | undefined,
|
|
// loading:bool | undefined}
|
|
//
|
|
setFormState(newState) {
|
|
//this returns a promise so any function that needs to wait for this can utilize that
|
|
return Vue.nextTick(function() {
|
|
if (newState.valid != undefined) {
|
|
newState.vm.formState.valid = newState.valid;
|
|
}
|
|
|
|
if (newState.dirty != undefined) {
|
|
newState.vm.formState.dirty = newState.dirty;
|
|
}
|
|
|
|
if (newState.loading != undefined) {
|
|
newState.vm.formState.loading = newState.loading;
|
|
}
|
|
|
|
if (newState.readOnly != undefined) {
|
|
newState.vm.formState.readOnly = newState.readOnly;
|
|
}
|
|
});
|
|
},
|
|
////////////////////////////////////
|
|
// Get form settings
|
|
// for form specified or empty object if there is none
|
|
// EAch form is responsible for what it stores and how it initializes, this just provides that
|
|
// the form does the actual work of what settings it requires
|
|
// Form settings are temp and saved, saved ones go into vuex and localstorage and temporary ones are stored in session storage
|
|
//
|
|
getFormSettings(formKey) {
|
|
return {
|
|
temp: JSON.parse(sessionStorage.getItem(formKey)),
|
|
saved: window.$gz.store.state.formSettings[formKey]
|
|
};
|
|
},
|
|
////////////////////////////////////
|
|
// Set form settings
|
|
// for form key specified
|
|
// requires object with one or both keys {temp:{...tempformsettings...},saved:{...persistedformsettings...}}
|
|
//
|
|
setFormSettings(formKey, formSettings) {
|
|
if (window.$gz.errorHandler.devMode()) {
|
|
if (!formSettings.saved && !formSettings.temp) {
|
|
throw "gzform:setFormSettings - saved AND temp keys are both missing from form data!";
|
|
}
|
|
}
|
|
|
|
if (formSettings.saved) {
|
|
window.$gz.store.commit("setFormSettings", {
|
|
formKey: formKey,
|
|
formSettings: formSettings.saved
|
|
});
|
|
}
|
|
|
|
if (formSettings.temp) {
|
|
sessionStorage.setItem(formKey, JSON.stringify(formSettings.temp));
|
|
}
|
|
}
|
|
};
|