import { html, customElement, css, state, when } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UMB_VALIDATION_CONTEXT, umbBindToValidation, UmbValidationContext } from '@umbraco-cms/backoffice/validation'; import type { UmbValidationMessage } from '@umbraco-cms/backoffice/validation'; @customElement('umb-example-validation-context-dashboard') export class UmbExampleValidationContextDashboardElement extends UmbLitElement { readonly validation = new UmbValidationContext(this); @state() name = ''; @state() email = ''; @state() city = ''; @state() country = ''; @state() messages?: UmbValidationMessage[]; @state() totalErrorCount = 0; @state() tab1ErrorCount = 0; @state() tab2ErrorCount = 0; @state() tab = '1'; constructor() { super(); this.consumeContext(UMB_VALIDATION_CONTEXT, (validationContext) => { this.observe( validationContext?.messages.messages, (messages) => { this.messages = messages; }, 'observeValidationMessages', ); }); // Observe all errors this.observe(this.validation.messages.messagesOfPathAndDescendant('$.form'), (value) => { this.totalErrorCount = [...new Set(value.map((x) => x.path))].length; }); // Observe errors for tab1, note that we only use part of the full JSONPath this.observe(this.validation.messages.messagesOfPathAndDescendant('$.form.tab1'), (value) => { this.tab1ErrorCount = [...new Set(value.map((x) => x.path))].length; }); // Observe errors for tab2, note that we only use part of the full JSONPath this.observe(this.validation.messages.messagesOfPathAndDescendant('$.form.tab2'), (value) => { this.tab2ErrorCount = [...new Set(value.map((x) => x.path))].length; }); } #onTabChange(e: Event) { this.tab = (e.target as HTMLElement).getAttribute('data-tab') as string; } #handleSave() { // fake server validation-errors for all fields if (this.name == '') this.validation.messages.addMessage( 'server', '$.form.tab1.name', 'Name server-error message', '4875e113-cd0c-4c57-ac92-53d677ba31ec', ); if (this.email == '') this.validation.messages.addMessage( 'server', '$.form.tab1.email', 'Email server-error message', 'a47e287b-4ce6-4e8b-8e05-614e7cec1a2a', ); if (this.city == '') this.validation.messages.addMessage( 'server', '$.form.tab2.city', 'City server-error message', '8dfc2f15-fb9a-463b-bcec-2c5d3ba2d07d', ); } #checkValidity(){ this.validation.validate().then( () => true, () => false, ); } override render() { return html` This is a demo of how the Validation Context can be used to validate a form with multiple steps. Start typing in the form or press Save to trigger validation.
Total errors: ${this.totalErrorCount}
Tab 1 ${when(this.tab1ErrorCount, () => html` ${this.tab1ErrorCount} `)} Tab 2 ${when(this.tab2ErrorCount, () => html` ${this.tab2ErrorCount} `)} ${when(this.tab == '1', () => html` ${this.#renderTab1()} `)} ${when(this.tab == '2', () => html` ${this.#renderTab2()} `)} Save
Check valid

Validation Context Messages

${JSON.stringify(this.messages ?? [], null, 3)}
`; } #renderTab1() { return html`
(this.name = (e.target as HTMLInputElement).value)} ${umbBindToValidation(this, '$.form.tab1.name', this.name)} required>
(this.email = (e.target as HTMLInputElement).value)} ${umbBindToValidation(this, '$.form.tab1.email', this.email)} required>
`; } #renderTab2() { return html`
(this.city = (e.target as HTMLInputElement).value)} ${umbBindToValidation(this, '$.form.tab2.city', this.city)} required>
(this.country = (e.target as HTMLInputElement).value)} required>
`; } static override styles = [ css` uui-badge { top: 0; right: 0; font-size: 10px; min-width: 17px; min-height: 17px; } label { display: block; } uui-box { margin: 20px; } uui-button { margin-top: 1rem; } pre { text-align: left; padding: 10px; border: 1px dotted #6f6f6f; background: #f2f2f2; font-size: 11px; line-height: 1.3em; } `, ]; } export default UmbExampleValidationContextDashboardElement; declare global { interface HTMLElementTagNameMap { 'umb-example-validation-context-dashboard': UmbExampleValidationContextDashboardElement; } }