Thanks for the making me understand the example. I tried something similar to your field at my end in 8.8 version. You can change according to 8.7 to see if it helps. Sharing the steps here based on my learning.
- Copy config/services/ui/ui.yaml at extensions\defaultExt\config\services\ui\ui.yaml and add a new regex (siren).
validations:
regex:
phone: "^([\\+]?|00)((([(]{0,1}\\s*[0-9]{1,4}\\s*[)]{0,1})\\s*)*|([\\-\\s\\./#x0-9])*)+$"
email: '^(?:[\.\-\+&#!\$\*=\?\^_`\{\}~\/\w]+)@(?:(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|\w+(?:[\.-]*\w+)*(?:\.[\w-]{2,})+)$'
siren: '^[0-9]{3}-[0-9]{2}$'
Please note that this is not a good way to add new config values, you can handle it in PHP code later.
- Create a new service at extensions\defaultExt\app\src\services\formatters\idnumber\idnumber-formatter.service.ts with the following code. Please create the directories if not exist
import {Injectable} from '@angular/core';
import {FormControlUtils,SystemConfigStore,Formatter} from 'core';
@Injectable({
providedIn: 'root'
})
export class IdNumberFormatter implements Formatter {
constructor(
protected formUtils: FormControlUtils,
protected systemConfigStore: SystemConfigStore
) {}
toUserFormat(value: string): string {
return value;
}
toInternalFormat(value: string): string {
return value;
}
getDefaultFormatPattern(): string {
const validations = this.systemConfigStore.getUi('validations');
const defaultRegex = validations?.regex?.siren || '';
return defaultRegex;
}
validateUserFormat(inputValue: any, regexPattern: string): boolean {
const trimmedInputValue = this.formUtils.getTrimmedInputValue(inputValue);
if (this.formUtils.isEmptyInputValue(trimmedInputValue)) {
return false;
}
const regex = new RegExp(regexPattern);
return !regex.test(trimmedInputValue);
}
}
Please note that we have used our regex value in getDefaultFormatPattern()
- Create validator file at extensions\defaultExt\app\src\services\record\validation\validators\id_number.validator.ts with following code. Create the directories if not exist.
import {AbstractControl} from '@angular/forms';
import {Injectable} from '@angular/core';
import {IdNumberFormatter} from '../../../formatters/idnumber/idnumber-formatter.service';
import {Record,StandardValidatorFn,StandardValidationErrors,ViewFieldDefinition,ValidatorInterface} from 'core';
export const idnumberValidator = (formatter: IdNumberFormatter, customValidationRegex?: string): StandardValidatorFn => (
(control: AbstractControl): StandardValidationErrors | null => {
const validationRegex = customValidationRegex || formatter.getDefaultFormatPattern();
const invalid = formatter.validateUserFormat(control.value, validationRegex);
return invalid ? {
emailValidator: {
valid: false,
format: new RegExp(validationRegex),
message: {
labelKey: 'ERR_INVALID_VALUE',
context: {
value: control.value,
expected: '000-00',
}
}
},
} : null;
}
);
@Injectable({
providedIn: 'root'
})
export class IdNumberValidator implements ValidatorInterface {
constructor(protected formatter: IdNumberFormatter) {
console.log('idnumber validator service 1');
}
applies(record: Record, viewField: ViewFieldDefinition): boolean {
if (!viewField || !viewField.fieldDefinition) {
return false;
}
return viewField.type === 'varchar' && viewField.name == 'siren_c';
}
getValidator(viewField: ViewFieldDefinition): StandardValidatorFn[] {
if (!viewField || !viewField.fieldDefinition) {
return [];
}
const customValidationRegex = viewField?.fieldDefinition?.validation?.regex.toString() ?? null;
return [idnumberValidator(this.formatter, customValidationRegex)];
}
}
Please note that we have used our formatted service here to check the input again the regex. Also note the label key ‘ERR_INVALID_VALUE’ you can add your own in public/legacy/custom/include/language/en_us.lang.php to display the validation message.
- Add references of these services in the extension.module.ts file at extensions\defaultExt\app\src\extension.module.ts
as
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
import {IdNumberValidator} from './services/record/validation/validators/id_number.validator';
import {IdNumberFormatter} from './services/formatters/idnumber/idnumber-formatter.service';
import{ValidationManager,DataTypeFormatter} from 'core';
@NgModule({ declarations: [], imports: [CommonModule], providers: [provideHttpClient(withInterceptorsFromDi())] })
export class ExtensionModule {
constructor(private dataTypeFormatter:DataTypeFormatter,
idnumValidator:IdNumberValidator,validationManager:ValidationManager,idnumberFormatter:IdNumberFormatter) {
console.log('extension module enabled');
dataTypeFormatter.map.idnumber = idnumberFormatter;
validationManager.registerFieldSaveValidator('accounts','varchar','siren_c',idnumValidator);
}
init(): void {
}
}
-
Enable the extension at extensions\defaultExt\config\extension.php as
'enabled' => true
, after remoteName property
-
Run php bin/console cache:clear to build the symfony package
-
Run the command yarn run build-dev:extension defaultExt
to build nodejs angular package
-
Reload the application to test the effect of code changes.

Please note that this is just an example of one field you can try making changes for similar fields in the validator file or as separate validators depending upon your requirements. Please take care of imports statements especially common module for your 8.7 version as the above example is working fine for 8.8 version. Thanks!