A coach view validation framework = one way to declare rules, one place to run them, one way to show results - implemented as a small JavaScript library (managed asset) plus a convention in the coaches. A compact working version:
// validation.js (managed web asset, included by the house-style header view)
window.myco = window.myco || {};
myco.validate = (function () {
var rules = {
required: function (v) { return v !== null && v !== undefined && String(v).trim() !== ""; },
email: function (v) { return !v || /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(v); },
min: function (v, p) { return v === null || v === "" || Number(v) >= p; },
max: function (v, p) { return v === null || v === "" || Number(v) <= p; },
pattern: function (v, p) { return !v || new RegExp(p).test(v); },
future: function (v) { return !v || new Date(v) >= new Date(new Date().setHours(0,0,0,0)); }
};
var messages = { required: "Required", email: "Invalid e-mail", min: "Minimum is {p}", max: "Maximum is {p}", pattern: "Invalid format", future: "Date must not be in the past" };
// spec: { "Email": ["required", "email"], "Amount": ["required", {"min": 1}, {"max": 10000}], "Due": ["future"] }
return function (spec, summaryControl) {
var errors = [];
Object.keys(spec).forEach(function (id) {
var ctl = bpmext.ui.getView(id); if (!ctl) return;
var value = ctl.getData ? ctl.getData() : null, msg = null;
spec[id].forEach(function (r) {
if (msg) return;
var name = typeof r === "string" ? r : Object.keys(r)[0], p = typeof r === "string" ? undefined : r[name];
if (!rules[name](value, p)) msg = messages[name].replace("{p}", p);
});
ctl.setValid(!msg, msg || ""); if (msg) errors.push((ctl.context.options["@label"] ? ctl.context.options["@label"].get("value") : id) + ": " + msg);
});
if (summaryControl) { summaryControl.setText(errors.join("<br>")); summaryControl.setVisible(errors.length > 0); }
return errors.length === 0;
};
})();// coach: Submit button > On click
if (!myco.validate({ "Email": ["required", "email"], "Amount": ["required", {"min": 1}, {"max": 10000}], "Due": ["future"] }, ${Errors})) return;
${SubmitHidden}.click(); // hidden button with the boundary eventExtend it with cross-field rules (a function in the spec), server-side re-validation in the CSHS validation script for the rules that matter (the server never trusts the browser), and a "validate on change" mode (call the same function from each control's On change with a one-control spec). Keep the messages in a resource bundle for localization. The framework stays small because the UI Toolkit already does the display part (setValid).
References