How to add validation to Text Field, using Angular Reactive Forms (custom validations) #5355
Replies: 2 comments 3 replies
-
In general, we recommend using Angular Material for dedicated Angular apps. Those components integrate much more tightly into Angular with regards to things like reactive forms. It's absolutely possible to use MWC in Angular, but there will be a steeper learning curve for integrations like this :) I would start with some Google searching for "using web components in angular reactive forms" to get some guides and articles to help you through the process. This article seems to be a good starting point. With regards to validation, my recommendation would be to ignore platform constraint validation APIs. So don't use these:
And instead use the manual validation attributes:
You can set these from Angular and have more control over the error state from Angular rather than using the browser's validation (Angular provides its own validator logic). |
Beta Was this translation helpful? Give feedback.
-
I created a wrap on Angular using the Web Component, I wanted to add a stackblitz with the solution, but web material does not work there, I already opened a discussion for that here. I will paste my code here so you can try to copy it.
<div>
<md-filled-text-field
#textField
[attr.name]="inputId"
[attr.id]="inputId"
[attr.label]="label"
[attr.pattern]="pattern"
[attr.aria-label]="ariaLabel || label"
[attr.supporting-text]="supportText"
[attr.type]="type"
[attr.value]="value || null"
[attr.placeholder]="placeholder"
[attr.min]="min || null"
[attr.max]="max || null"
[attr.maxlength]="maxlength || null"
[attr.minLength]="minLength || null"
[attr.required]="required || null"
[attr.disabled]="disabled || null"
[attr.error-text]="errorMessage"
[attr.error]="isInvalid || null"
>
</md-filled-text-field>
</div>
import {
AfterViewInit,
Component,
ElementRef,
Input,
OnInit,
ViewChild,
} from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-input-field',
templateUrl: './input-field.component.html',
styleUrl: './input-field.component.scss',
})
export class InputFieldComponent implements OnInit, AfterViewInit {
@Input() control!: FormControl;
@Input() value: unknown = '';
@Input() label: string = '';
@Input() placeholder: string = '';
@Input() ariaLabel: string = '';
@Input() pattern: string = '';
@Input() errorMessage: string = '';
@Input() supportText: string = '';
@Input() type: string = 'text';
@Input() required: boolean = false;
@Input() disabled: boolean = false;
@Input() min: number = 0;
@Input() max: number = 0;
@Input() maxlength: number = 0;
@Input() minLength: number = 0;
@ViewChild('textField') textFieldElement!: ElementRef;
inputId: string = '';
isInvalid: boolean = false;
constructor() {}
ngAfterViewInit(): void {
this.textFieldElement.nativeElement.addEventListener(
'input',
(event: InputEvent) => {
this.control.setValue((event.target as HTMLInputElement).value);
this._checkValidity();
}
);
this.textFieldElement.nativeElement.value = this._getValidDefaultValue(
this.control.value
);
}
ngOnInit() {
// Generate a unique inputId based on the label to connect label with input
this.inputId = this.label.toLowerCase().replace(/\s/g, '-');
if (!this.control) {
this.control = new FormControl();
}
// connect the value with the control in case there is an update from the outside
this.control.valueChanges.subscribe((value) => {
this.textFieldElement.nativeElement.value =
this._getValidDefaultValue(value);
this._checkValidity();
});
}
private _getValidDefaultValue(value: unknown) {
if (value === null || value === undefined) {
return '';
}
return value;
}
private _checkValidity() {
this.textFieldElement.nativeElement.reportValidity();
this.isInvalid = !this.textFieldElement.nativeElement.checkValidity();
}
} And to use the component wrapper, this is what I added: <form [formGroup]="myForm" (ngSubmit)="submitForm()">
<app-input-field
label="How many planets have you visited?"
[control]="getFormControl('planetsCount')"
errorMessage="That's a lie!"
[required]="false"
type="number"
[max]="1"
[min]="1"
supportText="Only in real life, dreaming doesn't count."
></app-input-field>
<div class="buttons">
<button type="submit">Submit</button>
<button type="button" (click)="resetForm()">Reset</button>
</div>
</form> And here is the TS part for the form import {
AfterViewInit,
Component,
ElementRef,
OnInit,
ViewChild,
} from '@angular/core';
import {
FormBuilder,
FormControl,
FormGroup,
Validators,
} from '@angular/forms';
@Component({
selector: 'app-form-validation',
templateUrl: './form-validation.component.html',
styleUrl: './form-validation.component.scss',
})
export class FormValidationComponent implements OnInit {
myForm!: FormGroup;
constructor(private formBuilder: FormBuilder) {}
ngOnInit(): void {
this.myForm = this.formBuilder.nonNullable.group({
planetsCount: [1, [Validators.required, Validators.max(1)]],
});
}
submitForm() {
if (this.myForm.valid) {
console.log('Form submitted:', this.myForm.value);
} else {
console.log('Form is invalid. Please fix the errors.');
}
}
resetForm() {
this.myForm.reset();
}
getFormControl(controlName: string): FormControl {
return this.myForm.get(controlName) as FormControl;
}
} On my solution here, I was able to connect the form with the wrapper and the web component, so the validation goes on both ways, kinda had to do the two-way data-binding manually, but it worked! Feel free to reach out if something is missing and it doesn't work. |
Beta Was this translation helpful? Give feedback.
-
Sorry, another beginner quesiton, as the docuentation mentions quite a lot, such as constraint validation, manual validation, reportValidity, and validity state - which without examples, for a beginner like me, don't make much sense : (
Is the former, using Angular Material, possible using Material Web?
Angular Materia with validation)l
Material Web (without validation)
How do I add the reactive form validations?
Validation
If I'm using Angular application logic, should I try to use manual validation....?
The documentation says:
Use the following properties and methods to check and report validation errors.
validity is the text field's current ValidityState.
setCustomValidity() sets a custom error message.
checkValidity() dispatches an invalid event.
reportValidity() dispatches an invalid event and displays the error in the text field's supporting text.
Manual validation
Link to “Manual validation”
Alternatively, text fields can manually control their error state and error message. Use manual validation if the text fields are driven by application state logic.
Prefer constraint validation when possible for more platform features, such as
validation and listening to invalid eventsBeta Was this translation helpful? Give feedback.
All reactions