entity limit client validation

This commit is contained in:
Niels Lyngsø
2024-05-24 11:27:56 +02:00
parent f8c2d08a1c
commit 480cab5f15
6 changed files with 63 additions and 14 deletions

View File

@@ -11,7 +11,11 @@ import { html, customElement, state, repeat, css, property, nothing } from '@umb
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import '../block-grid-entry/index.js';
import { UmbSorterController, type UmbSorterConfig, type resolvePlacementArgs } from '@umbraco-cms/backoffice/sorter';
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
import {
UmbFormControlMixin,
UmbFormControlValidator,
type UmbFormControlValidatorConfig,
} from '@umbraco-cms/backoffice/validation';
import type { UmbNumberRangeValueType } from '@umbraco-cms/backoffice/models';
/**
@@ -128,6 +132,7 @@ export class UmbBlockGridEntriesElement extends UmbFormControlMixin(UmbLitElemen
});
#context = new UmbBlockGridEntriesContext(this);
#controlValidator: UmbFormControlValidator;
@property({ attribute: false })
public set areaKey(value: string | null | undefined) {
@@ -198,33 +203,66 @@ export class UmbBlockGridEntriesElement extends UmbFormControlMixin(UmbLitElemen
'observeStylesheet',
);
});
this.#controlValidator = new UmbFormControlValidator(this, this /*, this.#dataPath*/);
}
async #setupValidation() {
async #getLimitValidation() {
if (this._areaKey === null) {
// This validation setup is not be as configurable as it should be, but it is a start. Alternatively we should consume the manager and observe the configuration. [NL]
const manager = await this.#context.getManager();
const config = manager.getEditorConfiguration();
const min = config?.getValueByAlias<UmbNumberRangeValueType>('validationLimit')?.min ?? 0;
const max = config?.getValueByAlias<UmbNumberRangeValueType>('validationLimit')?.max ?? Infinity;
return { min, max };
} else {
return { min: 3, max: 4 };
}
}
this.addValidator(
#rangeUnderflowValidator?: UmbFormControlValidatorConfig;
#rangeOverflowValidator?: UmbFormControlValidatorConfig;
async #setupValidation() {
const rangeLimit = await this.#getLimitValidation();
console.log('setup validation', this);
if (this.#rangeUnderflowValidator) {
this.removeValidator(this.#rangeUnderflowValidator);
this.#rangeUnderflowValidator = undefined;
}
if (rangeLimit.min !== 0) {
this.#rangeUnderflowValidator = this.addValidator(
'rangeUnderflow',
() => {
return this.localize.term('validation_entriesShort', [min, min - (this._layoutEntries.length ?? 0)]);
return this.localize.term(
'validation_entriesShort',
rangeLimit.min,
rangeLimit.min - (this._layoutEntries.length ?? 0),
);
},
() => {
return (this._layoutEntries.length ?? 0) < min;
return (this._layoutEntries.length ?? 0) < rangeLimit.min;
},
);
}
this.addValidator(
if (this.#rangeOverflowValidator) {
this.removeValidator(this.#rangeOverflowValidator);
this.#rangeOverflowValidator = undefined;
}
if (rangeLimit.max !== Infinity) {
this.#rangeOverflowValidator = this.addValidator(
'rangeOverflow',
() => {
return this.localize.term('validation_entriesExceed', [max, (this._layoutEntries.length ?? 0) - max]);
return this.localize.term(
'validation_entriesExceed',
rangeLimit.max,
(this._layoutEntries.length ?? 0) - rangeLimit.max,
);
},
() => {
return (this._layoutEntries.length ?? 0) > max;
return (this._layoutEntries.length ?? 0) > rangeLimit.max;
},
);
}
@@ -247,6 +285,7 @@ export class UmbBlockGridEntriesElement extends UmbFormControlMixin(UmbLitElemen
</umb-block-grid-entry>`,
)}
</div>
<uui-form-validation-message .for=${this}></uui-form-validation-message>
${this._canCreate ? this.#renderCreateButton() : nothing}
`;
}

View File

@@ -100,7 +100,7 @@ export class UmbPropertyEditorUIBlockGridElement
}
render() {
return html`<umb-block-grid-entries
return html` <umb-block-grid-entries
.areaKey=${null}
.layoutColumns=${this._layoutColumns}></umb-block-grid-entries>`;
}

View File

@@ -68,12 +68,13 @@ export class UmbValidationContext extends UmbContextBase<UmbValidationContext> i
const resultsStatus = await Promise.all(this.#validators.map((v) => v.validate())).then(
() => Promise.resolve(true),
() => Promise.reject(false),
() => Promise.resolve(false),
);
// If we have any messages then we are not valid, otherwise lets check the validation results: [NL]
// This enables us to keep client validations though UI is not present anymore — because the client validations got defined as messages. [NL]
const isValid = this.messages.getHasAnyMessages() ? false : resultsStatus;
this.#isValid = isValid;
if (isValid === false) {

View File

@@ -7,7 +7,7 @@ import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import type { UmbControllerAlias, UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
export class UmbFormControlValidator extends UmbControllerBase implements UmbValidator {
// The path to the data that this validator is validating. Public so the ValidationContext can access it.
// The path to the data that this validator is validating.
readonly #dataPath?: string;
#context?: typeof UMB_VALIDATION_CONTEXT.TYPE;

View File

@@ -34,7 +34,11 @@ interface UmbFormControlValidatorConfig {
}
export interface UmbFormControlMixinInterface<ValueType> extends HTMLElement {
addValidator: (flagKey: FlagTypes, getMessageMethod: () => string, checkMethod: () => boolean) => void;
addValidator: (
flagKey: FlagTypes,
getMessageMethod: () => string,
checkMethod: () => boolean,
) => UmbFormControlValidatorConfig;
removeValidator: (obj: UmbFormControlValidatorConfig) => void;
//static formAssociated: boolean;
//protected getFormElement(): HTMLElement | undefined | null; // allows for null as it makes it simpler to just implement a querySelector as that might return null. [NL]
@@ -56,7 +60,11 @@ export declare abstract class UmbFormControlMixinElement<ValueType>
{
protected _internals: ElementInternals;
protected _runValidators(): void;
addValidator: (flagKey: FlagTypes, getMessageMethod: () => string, checkMethod: () => boolean) => void;
addValidator: (
flagKey: FlagTypes,
getMessageMethod: () => string,
checkMethod: () => boolean,
) => UmbFormControlValidatorConfig;
removeValidator: (obj: UmbFormControlValidatorConfig) => void;
protected addFormControlElement(element: UmbNativeFormControlElement): void;
@@ -209,7 +217,7 @@ export function UmbFormControlMixin<
flagKey: flagKey,
getMessageMethod: getMessageMethod,
checkMethod: checkMethod,
};
} satisfies UmbFormControlValidatorConfig;
this.#validators.push(validator);
return validator;
}

View File

@@ -676,6 +676,7 @@ export class UmbDocumentWorkspaceContext
// Create the validation repository if it does not exist. (we first create this here when we need it) [NL]
this.#validationRepository ??= new UmbDocumentValidationRepository(this);
// We ask the server first to get a concatenated set of validation messages. So we see both front-end and back-end validation messages [NL]
if (this.getIsNew()) {
const parent = this.#parent.getValue();
if (!parent) throw new Error('Parent is not set');