用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/umbraco/Umbraco-CMS-Backoffice-Skills --skill umbraco-validation-context命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Run tests from skill examples and generate a report (project)
Validate links and references in SKILL.md files using deterministic scripts
Umbraco backoffice extension customisation - complete working examples showing how extension types combine
正在显示 SKILL.md
基于 SOC 职业分类
| 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 |
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.
Always fetch the latest docs before implementing:
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.
State Management: For observing validation state changes
umbraco-state-managementContext API: For consuming validation context
umbraco-context-apiimport { 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 {
// Create validation context for this component
readonly validation = new UmbValidationContext(this);
@state()
private _name = '';
@state()
private _email = '';
@state()
private _messages?: UmbValidationMessage[];
constructor() {
super();
// Observe all validation messages
this.consumeContext(UMB_VALIDATION_CONTEXT, () => {
.(
validationContext?..,
{
. = messages;
},
);
});
}
() {
html`;
}
#() {
isValid = ..();
(isValid) {
.();
}
}
}
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;
// Form fields
@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) {
.();
}
}
}
Add server validation errors after an API call:
async #handleSave() {
// First validate client-side
const isValid = await this.validation.validate();
if (!isValid) return;
try {
// Call API
const response = await this.#saveToServer();
if (!response.ok) {
// Add server validation errors
const errors = await response.json();
for (const error of errors.validationErrors) {
this.validation.messages.addMessage(
'server', // Source
error.path, // JSON Path (e.g., '$.form.name')
error.message, // Error message
crypto.randomUUID() // Unique key
);
}
}
} catch (error) {
console.error('Save failed:', error);
}
}
// Create context
const validation = new UmbValidationContext(this);
// Validate all bound fields
const isValid = await validation.validate();
// Access messages manager
validation.messages;
// Add a message
validation.messages.addMessage(source, path, message, key);
// Remove messages by source
validation.messages.removeMessagesBySource('server');
// Observe messages for a path and descendants
this.observe(
validation.messages.messagesOfPathAndDescendant('$.form.tab1'),
(messages) => { /* handle messages */ }
);
// Observe all messages
this.observe(
validation.messages.messages,
(messages) => { /* handle all messages */ }
);
// Bind an input to validation
${umbBindToValidation(this, '$.form.fieldName', fieldValue)}
Validation uses JSON Path to identify fields:
| Path | Description |
|---|---|
$.form | Root form object |
$.form.name | Name field |
$.form.tab1.email | Email field in tab1 |
$.form.items[0].value | First item's value |
$.form.items[*].name | All item names |
interface UmbValidationMessage {
source: string; // 'client' | 'server' | custom
path: string; // JSON Path
message: string; // Error message text
key: string; // Unique identifier
}
<uui-form-validation-message> around inputscrypto.randomUUID() for server error keysmessagesOfPathAndDescendant for scoped error countsThat's it! Always fetch fresh docs, keep examples minimal, generate complete working code.