Skip to main content
umbraco-validation-context Implement form validation using UmbValidationContext in Umbraco backoffice
설치로 이동 Skills Marketplace 커뮤니티가 만든 AI 스킬을 발견하고 탐색하세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/umbraco/Umbraco-CMS-Backoffice-Skills --skill umbraco-validation-context명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Zip 다운로드 다운로드 중... name umbraco-validation-context description Implement form validation using UmbValidationContext in Umbraco backoffice version 1.0.0 location managed allowed-tools Read, Write, Edit, WebFetch
Umbraco Validation Context
What is it?
UmbValidationContext provides a centralized validation system for forms in the Umbraco backoffice. It manages validation messages using JSON Path notation, supports both client-side and server-side validation, and enables reactive error counting for tabs and sections. This is essential for multi-step forms, workspace editors, and any UI that requires comprehensive validation feedback.
Documentation
Always fetch the latest docs before implementing:
Reference Examples
The Umbraco source includes working examples:
Validation Context Dashboard : /Umbraco-CMS/src/Umbraco.Web.UI.Client/examples/validation-context/
This example demonstrates multi-tab form validation with error counting.
Custom Validation Workspace Context : /Umbraco-CMS/src/Umbraco.Web.UI.Client/examples/custom-validation-workspace-context/
This example shows workspace-level validation patterns.
Related Foundation Skills
Workflow
Fetch docs - Use WebFetch on the URLs above
Ask questions - What fields? What validation rules? Multi-tab form?
Generate files - Create form element with validation context
Explain - Show what was created and how validation works
Basic Setup
import { html, customElement, state } 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 ('my-validated-form' )
export class MyValidatedFormElement extends UmbLitElement {
readonly validation = new UmbValidationContext (this );
@state ()
private _name = '' ;
@state ()
private _email = '' ;
@state ()
private _messages ?: UmbValidationMessage [];
constructor ( ) {
super ();
this .consumeContext (UMB_VALIDATION_CONTEXT , ( ) => {
. (
validationContext?. . ,
{
. = messages;
},
);
});
}
( ) {
html` ;
}
# ( ) {
isValid = . . ();
(isValid) {
. ( );
}
}
}
Multi-Tab Form with Error Counting
import { html, customElement, state, when } from '@umbraco-cms/backoffice/external/lit' ;
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element' ;
import { UmbValidationContext , umbBindToValidation } from '@umbraco-cms/backoffice/validation' ;
@customElement ('my-tabbed-form' )
export class MyTabbedFormElement extends UmbLitElement {
readonly validation = new UmbValidationContext (this );
@state () private _tab = '1' ;
@state () private _totalErrors = 0 ;
@state () private _tab1Errors = 0 ;
@state () private _tab2Errors = 0 ;
@state () private _name = '' ;
@state () private _email = '' ;
@state () private _city = '' ;
@state () private _country = '' ;
constructor ( ) {
super ();
. (
. . . ( ),
{
. = [... (messages. ( x. ))]. ;
}
);
. (
. . . ( ),
{
. = [... (messages. ( x. ))]. ;
}
);
. (
. . . ( ),
{
. = [... (messages. ( x. ))]. ;
}
);
}
( ) {
html` ;
}
# ( ) {
html` ;
}
# ( ) {
html` ;
}
# ( ) {
. = (e. ). ( ) ?? ;
}
# ( ) {
isValid = . . ();
(!isValid) {
. ( );
}
}
}
Server-Side Validation Errors
Add server validation errors after an API call:
async #handleSave ( ) {
const isValid = await this .validation .validate ();
if (!isValid) return ;
try {
const response = await this .#saveToServer ();
if (!response.ok ) {
const errors = await response.json ();
for (const error of errors.validationErrors ) {
this .validation .messages .addMessage (
'server' ,
error.path ,
error.message ,
crypto.randomUUID ()
);
}
}
} catch (error) {
console .error ('Save failed:' , error);
}
}
Key APIs
UmbValidationContext
const validation = new UmbValidationContext (this );
const isValid = await validation.validate ();
validation.messages ;
Validation Messages
validation.messages .addMessage (source, path, message, key);
validation.messages .removeMessagesBySource ('server' );
this .observe (
validation.messages .messagesOfPathAndDescendant ('$.form.tab1' ),
(messages ) => { }
);
this .observe (
validation.messages .messages ,
(messages ) => { }
);
umbBindToValidation Directive
${umbBindToValidation (this , '$.form.fieldName' , fieldValue)}
JSON Path Notation
Validation uses JSON Path to identify fields:
Path Description $.formRoot form object $.form.nameName field $.form.tab1.emailEmail field in tab1 $.form.items[0].valueFirst item's value $.form.items[*].nameAll item names
Validation Message Interface
interface UmbValidationMessage {
source : string ;
path : string ;
message : string ;
key : string ;
}
Best Practices
Use JSON Path hierarchy - Organize paths by tab/section for easy error counting
Wrap inputs - Use <uui-form-validation-message> around inputs
Clear server errors - Remove old server errors before new validation
Unique keys - Use crypto.randomUUID() for server error keys
Observe specific paths - Use messagesOfPathAndDescendant for scoped error counts
Show counts on tabs - Display error badges to guide users to problems
That's it! Always fetch fresh docs, keep examples minimal, generate complete working code.
validationContext
this
observe
messages
messages
(messages ) =>
this
_messages
'observeValidationMessages'
override
render
return
<uui-form >
<form >
<div >
<label > Name</label >
<uui-form-validation-message >
<uui-input
type ="text"
.value =
${this ._name}
@input =
${(e: InputEvent) => (this ._name = (e.target as HTMLInputElement).value)}
${umbBindToValidation(this , '$.form.name' , this ._name)}
required
> </uui-input >
</uui-form-validation-message >
</div >
<div >
<label > Email</label >
<uui-form-validation-message >
<uui-input
type ="email"
.value =
${this ._email}
@input =
${(e: InputEvent) => (this ._email = (e.target as HTMLInputElement).value)}
${umbBindToValidation(this , '$.form.email' , this ._email)}
required
> </uui-input >
</uui-form-validation-message >
</div >
<uui-button look ="primary" @click =
${this .#handleSave}
> Save</uui-button >
</form >
</uui-form >
<pre >
${JSON .stringify(this ._messages ?? [], null , 2 )}
</pre >
`
async
handleSave
const
await
this
validation
validate
if
console
log
'Form is valid!'
this
observe
this
validation
messages
messagesOfPathAndDescendant
'$.form'
(messages ) =>
this
_totalErrors
new
Set
map
(x ) =>
path
length
this
observe
this
validation
messages
messagesOfPathAndDescendant
'$.form.tab1'
(messages ) =>
this
_tab1Errors
new
Set
map
(x ) =>
path
length
this
observe
this
validation
messages
messagesOfPathAndDescendant
'$.form.tab2'
(messages ) =>
this
_tab2Errors
new
Set
map
(x ) =>
path
length
override
render
return
<uui-box >
<p > Total errors:
${this ._totalErrors}
</p >
<uui-tab-group @click =
${this .#onTabChange}
>
<uui-tab ?active =
${this ._tab === '1' }
data-tab ="1" >
Tab 1
${when(
this ._tab1Errors,
() => html`<uui-badge color ="danger" > ${this ._tab1Errors} </uui-badge > `
)}
</uui-tab >
<uui-tab ?active =
${this ._tab === '2' }
data-tab ="2" >
Tab 2
${when(
this ._tab2Errors,
() => html`<uui-badge color ="danger" > ${this ._tab2Errors} </uui-badge > `
)}
</uui-tab >
</uui-tab-group >
${when(this ._tab === '1' , () => this .#renderTab1())}
${when(this ._tab === '2' , () => this .#renderTab2())}
<uui-button look ="primary" @click =
${this .#handleSave}
> Save</uui-button >
</uui-box >
`
renderTab1
return
<uui-form >
<form >
<label > Name</label >
<uui-form-validation-message >
<uui-input
.value =
${this ._name}
@input =
${(e: InputEvent) => (this ._name = (e.target as HTMLInputElement).value)}
${umbBindToValidation(this , '$.form.tab1.name' , this ._name)}
required
> </uui-input >
</uui-form-validation-message >
<label > Email</label >
<uui-form-validation-message >
<uui-input
type ="email"
.value =
${this ._email}
@input =
${(e: InputEvent) => (this ._email = (e.target as HTMLInputElement).value)}
${umbBindToValidation(this , '$.form.tab1.email' , this ._email)}
required
> </uui-input >
</uui-form-validation-message >
</form >
</uui-form >
`
renderTab2
return
<uui-form >
<form >
<label > City</label >
<uui-form-validation-message >
<uui-input
.value =
${this ._city}
@input =
${(e: InputEvent) => (this ._city = (e.target as HTMLInputElement).value)}
${umbBindToValidation(this , '$.form.tab2.city' , this ._city)}
required
> </uui-input >
</uui-form-validation-message >
<label > Country</label >
<uui-form-validation-message >
<uui-input
.value =
${this ._country}
@input =
${(e: InputEvent) => (this ._country = (e.target as HTMLInputElement).value)}
required
> </uui-input >
</uui-form-validation-message >
</form >
</uui-form >
`
onTabChange
e : Event
this
_tab
target
as
HTMLElement
getAttribute
'data-tab'
'1'
async
handleSave
const
await
this
validation
validate
if
console
log
'Form has validation errors'