js/views/contact.js — cross-field validation
In the code column — js/views/contact.js. It uses lesson 11's attachLiveValidation, but with cross-field rules and a full submit lifecycle.
The cross-field part is rules(values):
function rules(values) {
const base = { /* name, email, subject, message: min 20 */ };
if (values.subject === 'bug') {
base.message = [required('Message'), minLength(50, 'A bug report'), maxLength(1000)];
}
return base;
}
Because attachLiveValidation calls getRules(values) with the current values each time, picking "Bug report" instantly makes the message rule stricter (min 50). Change the subject back — the field re-validates.
The submit lifecycle, inside submit:
submitBtn.disabled = true;
submitBtn.textContent = 'Sending…';
try {
await fakeSend(result.values);
// ...show the success panel...
} catch (error) {
showToast(error.message, 'error');
} finally {
submitBtn.disabled = false;
submitBtn.textContent = 'Send message';
}
finally matters: whether it succeeded or failed, the button always resets. Without it, after an error the button would stay "Sending…" and disabled forever.
And fakeSend — an honest simulation (it even "rejects" @example.com so you can see the error path):
// The real version: await fetch('/api/contact', { method:'POST', ... })
// The surrounding code doesn't change — only this function.
Tip. Notice that changing
subjectalso updates the hint under the field ("bug reports: 50+ characters"). A good form explains the rule before the user runs into it, not just shows an error after the fact.