Skip to content

[NAE-1892] Validation register #202

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: release/6.4.0
Choose a base branch
from
7 changes: 5 additions & 2 deletions projects/nae-example-app/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ import {
ViewService,
ProfileModule,
Dashboard,
FrontActionModule, NAE_ASYNC_RENDERING_CONFIGURATION
FrontActionModule,
NAE_ASYNC_RENDERING_CONFIGURATION,
ValidationRegistryService
} from '@netgrif/components-core';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {FlexLayoutModule, FlexModule} from '@angular/flex-layout';
Expand Down Expand Up @@ -267,7 +269,8 @@ export function HttpLoaderFactory(http: HttpClient) {
})
export class AppModule {

constructor(registry: ComponentRegistryService) {
constructor(registry: ComponentRegistryService,
validationRegistry: ValidationRegistryService) {
registry.register('email', (injector: Injector) => new ComponentPortal(EmailSubmissionFormComponent, null, injector));
registry.register('workflow-view', (injector: Injector) => new ComponentPortal(WorkflowViewExampleComponent, null, injector));
registry.register('task-view', (injector: Injector) => new ComponentPortal(TaskViewComponent, null, injector));
Expand Down
9 changes: 5 additions & 4 deletions projects/netgrif-components-core/src/assets/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -218,22 +218,23 @@
},
"dataField": {
"validations": {
"general": "Der Wert dieses Feldes ist ungültig!",
"required": "Dieses Feld ist ein Pflichtfeld!",
"requiredTrue": "Der eingegebene Wert muss wahr sein",
"odd": "Die eingegebene Nummer muss ungerade sein",
"even": "Die eingegebene Nummer muss gerade sein",
"positive": "Die eingegebene Nummer muss positiv sein",
"negative": "Die eingegebene Nummer muss negativ sein",
"decimal": "Die eingegebene Nummer muss ein Dezimalzahl sein",
"inrange": "Die eingegebene Nummer muss aus dem gegebenen Nummernbereich {{range}} sein",
"inrange": "Die eingegebene Nummer muss aus dem gegebenen Nummernbereich {{from}},{{to}} sein",
"minLength": "Der eingegebene Text muss länger als {{length}} Zeichen sein",
"maxLength": "Der eingegebene Text muss kurzer als {{length}} Zeichen sein",
"pattern": "Der eingegebene Text ist nicht im richtigen Format",
"phone": "Der eingegebene Text muss ein Telefonnummer sein",
"email": "Der eingegebene Text muss ein E-Mail sein",
"dateRange": "Das eingegebene Datum muss zwischen {{left}} und {{right}} liegen",
"datePast": "Das eingegebene Datum muss bis {{right}} sein",
"dateFuture": "Das eingegebene Datum muss ab {{left}} sein",
"dateRange": "Das eingegebene Datum muss zwischen {{from}} und {{to}} liegen",
"datePast": "Das eingegebene Datum muss bis {{from}} sein",
"dateFuture": "Das eingegebene Datum muss ab {{to}} sein",
"weekend": "Das eingegebene Datum muss das Wochenende sein",
"workday": "Das eingegebene Datum muss ein Wochentag sein",
"enumeration": "Eine von der angebotenen Optionen muss ausgewählt sein",
Expand Down
13 changes: 7 additions & 6 deletions projects/netgrif-components-core/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -218,28 +218,29 @@
},
"dataField": {
"validations": {
"general": "This value in this field is invalid!",
"required": "This field is required!",
"requiredTrue": "Entered value must be true",
"odd": "Entered number must be odd",
"even": "Entered number must be even",
"positive": "Entered number must be positive",
"negative": "Entered number must be negative",
"decimal": "Entered number must be decimal",
"inrange": "Entered number must be in range {{range}}",
"inrange": "Entered number must be in range {{from}},{{to}}",
"minLength": "Entered text must be at least {{length}} characters long",
"maxLength": "Entered text must be at most {{length}} characters long",
"pattern": "Entered text is in a wrong format",
"phone": "Entered text must be in telephone number format",
"email": "Entered text must be in email format",
"dateRange": "Entered date must be between {{left}} and {{right}}",
"datePast": "Entered date must be earlier than {{right}}",
"dateFuture": "Entered date must be later than {{left}}",
"dateRange": "Entered date must be between {{from}} and {{to}}",
"datePast": "Entered date must be earlier than {{to}}",
"dateFuture": "Entered date must be later than {{from}}",
"weekend": "Entered date must be weekend day",
"workday": "Entered date must be week day",
"enumeration": "One of the options must be selected",
"min": "Entered number must be greater than {{length}}",
"translationRequired": "Translation for languages: <{{translation}}> must be entered",
"translationOnly": "Only translation for languages: <{{translation}}> is allowed",
"translationRequired": "Translation for languages: <{{languages}}> must be entered",
"translationOnly": "Only translation for languages: <{{languages}}> is allowed",
"requiredI18n": "At least one value must be entered",
"requiredUserList": "At least one value must be entered"
},
Expand Down
9 changes: 5 additions & 4 deletions projects/netgrif-components-core/src/assets/i18n/sk.json
Original file line number Diff line number Diff line change
Expand Up @@ -218,22 +218,23 @@
},
"dataField": {
"validations": {
"general": "Hodnota tohto poľa je neplatná!",
"required": "Toto pole je potrebné!",
"requiredTrue": "Zadaná hodnota musí byť true",
"odd": "Zadané číslo musí byť nepárne",
"even": "Zadané číslo musí byť párne",
"positive": "Zadané číslo musí byť kladné",
"negative": "Zadané číslo musí byť záporné",
"decimal": "Zadané číslo musí byť decimálne",
"inrange": "Zadané číslo musí byť v rozsahu {{range}}",
"inrange": "Zadané číslo musí byť v rozsahu {{from}},{{to}}",
"minLength": "Zadaný text musí mať aspoň {{length}} znakov",
"maxLength": "Zadaný text môže mať maximálne {{length}} znakov",
"pattern": "Zadaný text je v nesprávnom formáte",
"phone": "Zadaný text musí byť tel. číslo",
"email": "Zadaný text musí byť email",
"dateRange": "Zadaný dátum musí byť v rozsahu {{left}} a {{right}}",
"datePast": "Zadaný dátum musí byť pred {{right}}",
"dateFuture": "Zadaný dátum musí byť po {{left}}",
"dateRange": "Zadaný dátum musí byť v rozsahu {{from}} a {{to}}",
"datePast": "Zadaný dátum musí byť pred {{to}}",
"dateFuture": "Zadaný dátum musí byť po {{from}",
"weekend": "Zadaný dátum musí byť víkendový deň",
"workday": "Zadaný dátum musí byť pracovný deň",
"enumeration": "Musí byť vybraná jedna z možností",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import {DATA_FIELD_PORTAL_DATA, DataFieldPortalData} from "../models/data-field-
import {DataField} from "../models/abstract-data-field";
import {FormControl} from "@angular/forms";
import {WrappedBoolean} from "../data-field-template/models/wrapped-boolean";
import {ValidationRegistryService} from "../../registry/validation-registry.service";
import {TranslateService} from "@ngx-translate/core";
import {FieldConverterService} from "../../task-content/services/field-converter.service";

@Component({
selector: 'ncc-base-data-field',
Expand All @@ -14,7 +17,9 @@ export abstract class AbstractBaseDataFieldComponent<T extends DataField<unknown
@Input() public formControlRef: FormControl;
@Input() public showLargeLayout: WrappedBoolean;

constructor(@Optional() @Inject(DATA_FIELD_PORTAL_DATA) dataFieldPortalData: DataFieldPortalData<T>) {
constructor(protected _translate?: TranslateService,
@Optional() @Inject(DATA_FIELD_PORTAL_DATA) dataFieldPortalData?: DataFieldPortalData<T>,
protected _validationRegistry?: ValidationRegistryService) {
if (!!dataFieldPortalData) {
this.dataField = dataFieldPortalData.dataField;
this.formControlRef = dataFieldPortalData.formControlRef;
Expand All @@ -25,11 +30,18 @@ export abstract class AbstractBaseDataFieldComponent<T extends DataField<unknown
}
}
}

ngOnDestroy(): void {
this.dataField.disconnectFormControl();
}

get validationRegistry(): ValidationRegistryService {
return this._validationRegistry;
}

get translate(): TranslateService {
return this._translate;
}

public checkPropertyInComponent(property: string): boolean {
return !!this.dataField?.component?.properties
&& property in this.dataField.component.properties;
Expand All @@ -42,4 +54,34 @@ export abstract class AbstractBaseDataFieldComponent<T extends DataField<unknown
public hasHint(): boolean {
return this.dataField.description !== undefined && this.dataField.description !== '';
}

public getErrorMessage() {
if (!!this.validationRegistry) {
const validators = this.validationRegistry.registry.get(FieldConverterService.resolveType(this.dataField));
for (const validator of validators.values()) {
if (this.formControlRef.hasError(validator.validationErrorKey)) {
const validation = this.dataField.validations.find(v => v.name === validator.key);
const args = [];
const messageParams = {};
validator.attributeNames.forEach(atr => {
const attributeValue = validation.arguments[atr].value;
messageParams[atr] = attributeValue;
args.push(attributeValue)
})
return this.resolveErrorMessage(validator.key, this.translate.instant(validator.defaultErrorMessage(...args), messageParams));
}
}
}
return '';
}

private resolveErrorMessage(search: string, generalMessage: string) {
if (!!this.dataField.validations) {
const validation = this.dataField.validations.find(value => value.name === search);
if (validation.validationMessage && validation.validationMessage !== '') {
return validation.validationMessage;
}
}
return generalMessage;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import {MaterialModule} from '../../material/material.module';
import {AbstractBooleanFieldComponent} from './abstract-boolean-field.component';
import {BooleanField} from './models/boolean-field';
import {NAE_INFORM_ABOUT_INVALID_DATA} from '../models/invalid-data-policy-token';
import {Validator} from "../../registry/model/validator";
import {requiredTrueValidation} from "../models/validation-functions";
import {DataFieldsModule} from "../data-fields.module";

describe('AbstractBooleanFieldComponent', () => {
let component: TestBooleanComponent;
Expand All @@ -28,7 +31,8 @@ describe('AbstractBooleanFieldComponent', () => {
AngularResizeEventModule,
TranslateLibModule,
HttpClientTestingModule,
NoopAnimationsModule
NoopAnimationsModule,
DataFieldsModule
],
providers: [
{provide: AuthenticationMethodService, useClass: MockAuthenticationMethodService},
Expand Down Expand Up @@ -77,5 +81,8 @@ class TestWrapperComponent {
}, undefined,
undefined,
undefined,
[{validationRule: 'requiredTrue', validationMessage: 'this is custom message'}]);
[{name: 'requiredTrue', validationMessage: 'this is custom message'}],
undefined,
undefined,
new Map<string, Validator>([['requiredTrue', requiredTrueValidation]]));
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ import {AbstractBooleanDefaultFieldComponent} from "./abstract-boolean-default-f
import {DATA_FIELD_PORTAL_DATA, DataFieldPortalData} from "../../models/data-field-portal-data-injection-token";
import {FormControl} from "@angular/forms";
import {WrappedBoolean} from "../../data-field-template/models/wrapped-boolean";
import {ValidationRegistryService} from "../../../registry/validation-registry.service";
import {requiredTrueValidation} from "../../models/validation-functions";
import {Validator} from "../../../registry/model/validator";
import {DataFieldsModule} from "../../data-fields.module";

describe('AbstractBooleanDefaultFieldComponent', () => {
let component: TestBooleanComponent;
Expand All @@ -31,7 +35,8 @@ describe('AbstractBooleanDefaultFieldComponent', () => {
AngularResizeEventModule,
TranslateLibModule,
HttpClientTestingModule,
NoopAnimationsModule
NoopAnimationsModule,
DataFieldsModule
],
providers: [
{provide: AuthenticationMethodService, useClass: MockAuthenticationMethodService},
Expand All @@ -44,7 +49,10 @@ describe('AbstractBooleanDefaultFieldComponent', () => {
}, undefined,
undefined,
undefined,
[{validationRule: 'requiredTrue', validationMessage: 'this is custom message'}]),
[{name: 'requiredTrue', validationMessage: 'this is custom message'}],
undefined,
undefined,
new Map<string, Validator>([['requiredTrue', requiredTrueValidation]])),
formControlRef: new FormControl(),
showLargeLayout: new WrappedBoolean()
} as DataFieldPortalData<BooleanField>
Expand Down Expand Up @@ -82,8 +90,9 @@ describe('AbstractBooleanDefaultFieldComponent', () => {
class TestBooleanComponent extends AbstractBooleanDefaultFieldComponent {

constructor(translate: TranslateService,
@Optional() @Inject(DATA_FIELD_PORTAL_DATA) dataFieldPortalData: DataFieldPortalData<BooleanField>) {
super(translate, dataFieldPortalData);
@Optional() @Inject(DATA_FIELD_PORTAL_DATA) dataFieldPortalData: DataFieldPortalData<BooleanField>,
_validationRegistry: ValidationRegistryService) {
super(translate, dataFieldPortalData, _validationRegistry);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,23 @@
import {Component, Inject, Optional} from "@angular/core";
import {BooleanField, BooleanFieldValidation} from "../models/boolean-field";
import {BooleanField} from "../models/boolean-field";
import {TranslateService} from "@ngx-translate/core";
import {DATA_FIELD_PORTAL_DATA, DataFieldPortalData} from "../../models/data-field-portal-data-injection-token";
import {AbstractBaseDataFieldComponent} from "../../base-component/abstract-base-data-field.component";
import {ValidationRegistryService} from "../../../registry/validation-registry.service";

@Component({
selector: 'ncc-abstract-boolean-default-field',
template: ''
})
export abstract class AbstractBooleanDefaultFieldComponent extends AbstractBaseDataFieldComponent<BooleanField> {

constructor(protected _translate: TranslateService,
@Optional() @Inject(DATA_FIELD_PORTAL_DATA) dataFieldPortalData: DataFieldPortalData<BooleanField>) {
super(dataFieldPortalData);
}

public getErrorMessage() {
if (this.formControlRef.hasError(BooleanFieldValidation.REQUIRED)) {
return this._translate.instant('dataField.validations.required');
} else if (this.formControlRef.hasError(BooleanFieldValidation.REQUIRED_TRUE)) {
return this.resolveErrorMessage(this.dataField, BooleanFieldValidation.REQUIRED_TRUE,
this._translate.instant('dataField.validations.requiredTrue'));
}
return '';
constructor(_translate: TranslateService,
@Optional() @Inject(DATA_FIELD_PORTAL_DATA) dataFieldPortalData: DataFieldPortalData<BooleanField>,
_validationRegistry: ValidationRegistryService) {
super(_translate, dataFieldPortalData, _validationRegistry);
}

public createValueLabel(): string {
return this._translate.instant('dataField.values.boolean.' + this.dataField.value);
}

private resolveErrorMessage(dataField: BooleanField, search: string, generalMessage: string) {
const validation = dataField.validations.find(value => value.validationRule.includes(search));
if (validation.validationMessage && validation.validationMessage !== '') {
return validation.validationMessage;
}
return generalMessage;
return this.translate.instant('dataField.values.boolean.' + this.dataField.value);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {Behavior} from '../../models/behavior';
import {AbstractControl, ValidationErrors, ValidatorFn} from '@angular/forms';
import {Layout} from '../../models/layout';
import {Validation} from '../../models/validation';
import {Component, ComponentPrefixes} from '../../models/component';
import {DataField} from '../../models/abstract-data-field';
import {Validator} from "../../../registry/model/validator";
import {UpdateOnStrategy, UpdateStrategy} from "../../models/update-strategy";

export enum BooleanFieldValidation {
Expand All @@ -14,8 +14,10 @@ export enum BooleanFieldValidation {
export class BooleanField extends DataField<boolean> {

constructor(stringId: string, title: string, value: boolean, behavior: Behavior, placeholder?: string,
description?: string, layout?: Layout, validations?: Array<Validation>, component?: Component, parentTaskId?: string) {
super(stringId, title, value, behavior, placeholder, description, layout, validations, component, parentTaskId);
description?: string, layout?: Layout, validations?: Array<Validation>, component?: Component, parentTaskId?: string,
validatorRegister?: Map<string, Validator>) {
super(stringId, title, value, behavior, placeholder, description, layout, validations, component, parentTaskId,
undefined, validatorRegister);
}

public getTypedComponentType(): string {
Expand All @@ -25,20 +27,4 @@ export class BooleanField extends DataField<boolean> {
public getUpdateOnStrategy(): UpdateOnStrategy {
return UpdateStrategy.CHANGE;
}

protected resolveValidations(): Array<ValidatorFn> {
const result = [];

this.validations.forEach(item => {
if (item.validationRule.includes(BooleanFieldValidation.REQUIRED_TRUE)) {
result.push(this.requiredTrue);
}
});

return result;
}

private requiredTrue(control: AbstractControl): ValidationErrors | null {
return control.value === true ? null : {requiredTrue: true};
}
}
Loading
Loading