The coach's validate event (Coach > Behavior > Validation on older releases; on CSHS the Validation boundary of a coach or the client-side script bound to the coach's validation) runs before the coach is submitted; you mark controls invalid and the coach stays on page. Two flavours:
1. Client-side validation script of a CSHS coach (Coach > Properties > Validation > "validate with a client-side script"):
// runs in the browser when a button fires a boundary event with "validate" enabled
var errors = [];
if (!tw.local.customer.email || !/^[^@]+@[^@]+\.[^@]+$/.test(tw.local.customer.email)) errors.push({ path: "tw.local.customer.email", message: "Enter a valid e-mail" });
if (tw.local.order.amount > 10000 && !tw.local.order.approver) errors.push({ path: "tw.local.order.approver", message: "Approver required above 10,000" });
// report: every control bound to the path shows the message (System Data "CoachValidation")
tw.system.coachValidation.clearValidationErrors();
errors.forEach(function (e) { tw.system.coachValidation.addValidationError(e.path, e.message); });
// returning errors keeps the coach open; no errors -> the flow continues2. Control-level validation in the coach (UI Toolkit) - the Validate event of a control, or the button's handler before firing the boundary event:
// Button > On click
var ok = true;
if (${Email}.getData() === "") { ${Email}.setValid(false, "E-mail is required"); ok = false; } else ${Email}.setValid(true);
if (${Amount}.getData() > 10000) { ${Amount}.setValid(false, "Above the limit, needs approval"); ok = false; }
if (ok) ${Submit}.click(); else ${Status}.setText("Fix the highlighted fields"); // Submit = hidden button with the boundary eventServer-side (heritage services) the same object is tw.system.coachValidation.addValidationError("tw.local.x", "msg") inside the server script attached to the coach's validation. The path must be the exact binding path of the control (list items: tw.local.lines[2].qty).
References