I've tried this before - MatFormField picks up MatError
s via ContentChildren
. Since it's content projection, dynamically adding or removing elements like MatError
doesn't work -- the content template is compiled and handled differently than other templates.
You mentioned in a comment that you're trying to handle consistent error messaging across templates...why not add a directive
to the <mat-error>
element that controls the error messaging based on the parent control's form validation state?
If you're looking to control templates, though, you can't use a directive. You can, however, create a component and use it like a directive:
Create new file mat-error-messages.component.ts
, this is full code of file:
import {AfterViewInit, Component, Injector} from '@angular/core';
import {MatFormField, MatFormFieldControl} from '@angular/material/form-field';
import {MatInput} from '@angular/material/input';
@Component({
selector: '[matErrorMessages]',
template: '{{ error }}'
})
export class MatErrorMessagesComponent implements AfterViewInit {
public error = '';
private inputRef: MatFormFieldControl<MatInput>;
constructor(private _inj: Injector) {
}
public ngAfterViewInit(): void {
// grab reference to MatFormField directive, where form control is accessible.
const container = this._inj.get(MatFormField);
this.inputRef = container._control;
// sub to the control's status stream
this.inputRef.ngControl.statusChanges.subscribe(this.updateErrors);
}
private updateErrors = (state: 'VALID' | 'INVALID'): void => {
if (state === 'INVALID') {
// active errors on the FormControl
const controlErrors = this.inputRef.ngControl.errors;
// just grab one error
const firstError = Object.keys(controlErrors)[0];
if (firstError === 'required')
{this.error = 'This field is required.';}
if (firstError === 'minlength')
{this.error = 'This field should be longer.';}
if (firstError === 'error from my own custom validator')
{this.error = 'You get the point.';}
// .....
}
};
}
then in template....
<mat-error matErrorMessages></mat-error>
This way, you let the MatFormField
control the presence of the MatError
as it's supposed to do, but you control the content of the error element in a localized, clean way.
stackblitz of the above: https://stackblitz.com/edit/angular-sjkfft
You have to dig in to the Material components' private members, which is questionable practice, and can break with library updates, but it works.
I actually have a repo for this type of error messaging that was written for a much older version of @angular/material, but I was also able to get a hold of the Validator
itself for better error messaging, and was injecting a whole list of custom validators/errors for that validator into the module: https://github.com/joh04667/material-error-messages
mat-form-field
only needs to have onemat-error
- it is simple to dynamically change the error content. – ScadMatFormField
has gone through some changes – Knaverymat-error
to every form field when it could be dynamically generated on demand. We can absolutely have amat-error
and then apply a method{{ getError('formGroup.formControl') }}
, and do a look up of the error message, but we'd be adding that method to every component and it just adds template bloat. – ScottieControlErrorComponent
or dynamically loading themat-error
component and have it content project properly. – Scottie<mat-error>{{ getError('formGroup.formControl') }}</mat-error>
to<mat-form-field>
better than dynamically adding<span>{{ getError('formGroup.formControl') }}</span>
to<mat-error>
? – Scadmat-form-field
which handles the error handling. – Brigitte