41
loading...
This website collects cookies to deliver better user experience
Automatic validation messages for Angular forms in any language
<label>Email:
<input formControlName="email">
</label>
<div *ngIf="myForm.controls['email'].invalid">
<div *ngIf="myForm.controls['email'].errors.required">
{{ translate('email.require') }}
</div>
</div>
ngx-translation-validation
. With this package you can write the following.<label>Email:
<input formControlName="email">
</label>
At the time of writing it does only support Transloco and not ngx-translate, but I plan to add this. Also other translation libraries should be easy to add.
<div *ngIf="myForm.controls['email'].errors.required">
{{ translate('email.require') }}
</div>
formControl
and formControlName
. This way the directive is automatically applied to all inputs having one of those. Inside the directive we subscribe to the statusChanges
of the formControl
. The statusChanges
is A multicasting observable that emits an event every time the
validation status
of the control recalculates.
status
of the control is recalculated according the updateOn
property of the AbstractControlOptions
.constructor(@Self controlDir: NgControl){}
...
this.controlDir.statusChanges.subscribe(
() => {
const controlErrors = this.controlDir.errors;
if (controlErrors) {
const firstKey = Object.keys(controlErrors)[0];
this.setError(`${this.config.type}.${this.controlDir.name}.${firstKey}`);
} else if (this.ref) {
this.setError(null);
}
}
);
status
of a control changes we check if the control is valid. If there is an error we will call the setError
method with the translation key for the first error on the control. This function should create a component and display the error message. The translation key is formatted like this type
.controlName
.errorKey
. type
is the type of validation, which defaults to validation
just to set it apart from any other translation stringscontrolName
is the name of the form controlerrorKey
is the name of the validation constraintvalidation.name.required
is the translation key for the name
form control that has Validation.required
as constraint.const factory = this.resolver.resolveComponentFactory(this.config.errorsComponent);
this.ref = this.container.createComponent(factory);
this.ref.instance.text = errorText;
<ng-container *transloco="let t">
<p class="input-errors">
{{ t(text) }}
</p>
</ng-container>
this.ref.instance.text
is an @Input() text
on the component after which the error text can be used in the template. The error text is an translation string and so we use it inside the transloco translation function.validation.name.required
probably won't do it, because name
will the a form control of many forms (eg. project name, user name, company name). You probably want something like validation.userForm.name.required
. To achieve this we must be able to set the scope for a form or a group of form controls.<form [formGroup]="form" ngxTvScope="userForm">
<div>
<label for="name">{{ translate('name') }}</label>
<input formControlName="name" id="name" />
</div>
</form>
ngxTvScope
is a directive with the sole purpose of relaying the scope to generate the correct translation string. The translation string then becomes type
.scope
.controlName
.errorKey
.@Host()
which limits the search up to the host element, which means that it searches inside the component where the form control lives angular docs.constructor(
@Optional() @Host() controlScope?: NgxTvScopeDirective
){}
...
this.scope = this.controlScope?.scope ?? this.config.defaultScope;
...
this.setError(`${this.config.type}.${this.scope}.${this.controlDir.name}.${firstKey}`);
AppModule
, import it in the module your component lives in and then all your form elements will show validation messages. Also check out the example here or if you want to have a look at the code checkout the Stackblitz.