Merge pull request #2444 from enkelmedia/example-validation-context-server-error
Fix for issue with multiple server-validation errors
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import type { ManifestDashboard } from '@umbraco-cms/backoffice/dashboard';
|
||||
|
||||
const dashboard : ManifestDashboard = {
|
||||
type: 'dashboard',
|
||||
alias: 'Demo.Dashboard',
|
||||
name: 'Demo Dashboard Validation Context',
|
||||
weight: 1000,
|
||||
element: () => import('./validation-context-dashboard.js'),
|
||||
meta: {
|
||||
label: 'Validation Context Demo',
|
||||
pathname: 'demo'
|
||||
},
|
||||
conditions : [
|
||||
{
|
||||
alias : "Umb.Condition.SectionAlias",
|
||||
match : "Umb.Section.Content"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const manifests = [
|
||||
dashboard
|
||||
];
|
||||
@@ -0,0 +1,219 @@
|
||||
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';
|
||||
|
||||
@customElement('umb-example-validation-context-dashboard')
|
||||
export class UmbExampleValidationContextDashboard extends UmbLitElement {
|
||||
|
||||
readonly validation = new UmbValidationContext(this);
|
||||
|
||||
@state()
|
||||
name = '';
|
||||
|
||||
@state()
|
||||
email = '';
|
||||
|
||||
@state()
|
||||
city = '';
|
||||
|
||||
@state()
|
||||
country = '';
|
||||
|
||||
@state()
|
||||
messages? : any[]
|
||||
|
||||
@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.validation.messages.messagesOfPathAndDescendant('$.form').subscribe((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.validation.messages.messagesOfPathAndDescendant('$.form.tab1').subscribe((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.validation.messages.messagesOfPathAndDescendant('$.form.tab2').subscribe((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');
|
||||
if(this.country == '')
|
||||
this.validation.messages.addMessage('server','$.form.tab2.country','Country server-error message','d98624f6-82a2-4e94-822a-776b44b01495');
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<uui-box>
|
||||
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.
|
||||
<hr/>
|
||||
Total errors: ${this.totalErrorCount}
|
||||
<hr/>
|
||||
<uui-tab-group @click=${this.#onTabChange}>
|
||||
<uui-tab ?active=${this.tab == '1'} data-tab="1">
|
||||
Tab 1
|
||||
${when(this.tab1ErrorCount,()=>html`
|
||||
<uui-badge color="danger">${this.tab1ErrorCount}</uui-badge>
|
||||
`)}
|
||||
</uui-tab>
|
||||
<uui-tab ?active=${this.tab == '2'} data-tab="2">
|
||||
Tab 2
|
||||
${when(this.tab2ErrorCount,()=>html`
|
||||
<uui-badge color="danger">${this.tab2ErrorCount}</uui-badge>
|
||||
`)}
|
||||
</uui-tab>
|
||||
</uui-tab-group>
|
||||
|
||||
${when(this.tab=='1',()=>html`
|
||||
${this.#renderTab1()}
|
||||
`)}
|
||||
${when(this.tab=='2',()=>html`
|
||||
${this.#renderTab2()}
|
||||
`)}
|
||||
|
||||
<uui-button look="primary" color="positive" @click=${this.#handleSave}>Save</uui-button>
|
||||
<hr/>
|
||||
<h3>Validation Context Messages</h3>
|
||||
<pre>${JSON.stringify(this.messages ?? [],null,3)}</pre>
|
||||
</uui-box>
|
||||
`
|
||||
}
|
||||
|
||||
#renderTab1() {
|
||||
return html`
|
||||
<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.tab1.name',this.name)}
|
||||
required></uui-input>
|
||||
</uui-form-validation-message>
|
||||
</div>
|
||||
<label>E-mail</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 html`
|
||||
<uui-form>
|
||||
<form>
|
||||
<div>
|
||||
<label>City</label>
|
||||
<uui-form-validation-message>
|
||||
<uui-input
|
||||
type="text"
|
||||
.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>
|
||||
</div>
|
||||
<label>Country</label>
|
||||
<uui-form-validation-message>
|
||||
<uui-input
|
||||
type="text"
|
||||
.value=${this.country}
|
||||
@input=${(e: InputEvent)=>this.country = (e.target as HTMLInputElement).value}
|
||||
${umbBindToValidation(this,'$.form.tab2.country',this.country)}
|
||||
required></uui-input>
|
||||
</uui-form-validation-message>
|
||||
</form>
|
||||
</uui-form>
|
||||
`
|
||||
}
|
||||
|
||||
|
||||
|
||||
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 UmbExampleValidationContextDashboard;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-example-validation-context-dashboard': UmbExampleValidationContextDashboard;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
import type { UmbValidationMessage } from '../context/validation-messages.manager.js';
|
||||
import { UMB_VALIDATION_CONTEXT } from '../context/validation.context-token.js';
|
||||
import type { UmbFormControlMixinInterface } from '../mixins/form-control.mixin.js';
|
||||
import { defaultMemoization } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { defaultMemoization, simpleHashCode } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
const ctrlSymbol = Symbol();
|
||||
const observeSymbol = Symbol();
|
||||
|
||||
/**
|
||||
@@ -13,6 +12,7 @@ const observeSymbol = Symbol();
|
||||
* This controller will add a custom error to the form control if the validation context has any messages for the specified data path.
|
||||
*/
|
||||
export class UmbBindServerValidationToFormControl extends UmbControllerBase {
|
||||
|
||||
#context?: typeof UMB_VALIDATION_CONTEXT.TYPE;
|
||||
|
||||
#control: UmbFormControlMixinInterface<unknown>;
|
||||
@@ -41,7 +41,7 @@ export class UmbBindServerValidationToFormControl extends UmbControllerBase {
|
||||
}
|
||||
|
||||
constructor(host: UmbControllerHost, formControl: UmbFormControlMixinInterface<unknown>, dataPath: string) {
|
||||
super(host, ctrlSymbol);
|
||||
super(host,'umbFormControlValidation_'+simpleHashCode(dataPath));
|
||||
this.#control = formControl;
|
||||
this.consumeContext(UMB_VALIDATION_CONTEXT, (context) => {
|
||||
this.#context = context;
|
||||
|
||||
Reference in New Issue
Block a user