Validation errors for list items need the exact binding path of the item, including the index, otherwise the framework cannot find a control to attach the message to and the error is dropped (or shown only in the summary). Two working forms:
// server-side coach validation script (heritage / or the CSHS validation script): one error per invalid row
tw.system.coachValidation.clearValidationErrors();
for (var i = 0; i < tw.local.order.lines.listLength; i++) {
var l = tw.local.order.lines[i];
if (l.qty <= 0) tw.system.coachValidation.addValidationError("tw.local.order.lines[" + i + "].qty", "Quantity must be greater than 0");
if (!l.item) tw.system.coachValidation.addValidationError("tw.local.order.lines[" + i + "].item", "Select an item");
}
if (tw.local.order.lines.listLength === 0) tw.system.coachValidation.addValidationError("tw.local.order.lines", "Add at least one line"); // the table itself// client side (UI Toolkit table "Lines" with columns Qty / Item): mark the cell controls of row i
var t = ${Lines}, ok = true;
t.getData().forEach(function (l, i) {
var qty = page.ui.get("Lines[" + i + "]/Qty"), item = page.ui.get("Lines[" + i + "]/Item");
if (qty) { var bad = !(l.qty > 0); qty.setValid(!bad, "Quantity must be greater than 0"); ok = ok && !bad; }
if (item) { var miss = !l.item; item.setValid(!miss, "Select an item"); ok = ok && !miss; }
});
if (!ok) return false; // stop the boundary eventWhy it "does not work" typically: the path used tw.local.lines.qty (no index), the table's column controls are bound to currentItem.qty so the framework maps tw.local.order.lines[i].qty only when the row is rendered (rows on other pages of a paged table are not rendered - the error is kept but not visible until that page is shown), or the validation ran on a variable that the coach does not bind at all. Also the coach's validation error banner lists every error regardless of the path, so add one summary line for the table when rows may be off-screen.
References