问题
I am using Angular 8 and I am trying to create a registration form with reactive forms. I had created some alert messages to show validation errors.
However, the previous alert is shown instead of the current alert! (one step late)
app.component.ts file:
import { Component } from '@angular/core';
import { FormBuilder, Validators, FormControl } from '@angular/forms';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
form = this.fBuilder.group({
userName: ['', [
Validators.required,
Validators.minLength(3),
this.userNameValidation.bind(this)
]]
});
requiredError: boolean;
minLengthError: boolean;
constructor(private fBuilder: FormBuilder) { }
// User Name Validation
userNameValidation(control: FormControl) {
if (control.hasError('required') && control.dirty) {
this.requiredError = true;
} else {
this.requiredError = false;
}
if (control.hasError('minlength') && control.dirty) {
this.minLengthError = true;
} else {
this.minLengthError = false;
}
}
}
app.component.html file:
<form [formGroup]="form">
<label>User Name: </label>
<input formControlName="userName" type="text">
</form>
<!-- alerts -->
<p *ngIf="requiredError">User Name is required</p>
<p *ngIf="minLengthError">User Name must be at least 3 characters</p>
Here is a simple StackBlitz showing what I mean:
Stackblitz Example 🚀🚀
Try typing one letter, then delete it and see what happens.
Thanks in advance!
回答1:
Based on the comment on other answer, you want to "send errors to another component". If you indeed need the custom validator for that, we need to remember that angular forms are in fact asynchronous, so it takes an extra tick to form being correctly evaluated. I would never recommend to use setTimeout
in asynchronous functions, but we can know that the form values have been set after waiting a tick, so it is safe to use. Therefore wrap everything inside a timeout:
setTimeout(() => {
if (control.hasError("required") && control.dirty) {
this.requiredError = true;
} else {
this.requiredError = false;
}
if (control.hasError("minlength") && control.dirty) {
this.minLengthError = true;
} else {
this.minLengthError = false;
}
});
STACKBLITZ
But if you have a child component like <hello></hello>
in your template and you want to send the errors to that child via @Input
, you don't need to use the above approach, you can also just send the boolean value of the validity to child, like...
<hello *ngIf="form.get('userName').dirty" [requiredError]="form.hasError('required', 'userName')"></hello>
and catch that in your child with for example:
@Input() requiredError: boolean;
ngOnChanges() {
if (this.requiredError) {
alert('field required!')
}
}
STACKBLITZ
回答2:
You don't need a custom validator to display error in your forms. What you are doing with this.userNameValidation.bind(this)
is creating a custom validator.
What you can do is create a variable in your component (.ts file) to reference the form control in your form group.
In this instance, get userName() { return this.form.get('userName'); }
.
This creates a variable userName
for your template to "get". Subsequently, you can use this variable to check for errors like the following:
template .html file:
<form [formGroup]="form">
<label for="userName">User Name: </label>
<input type="text" formControlName="userName" name="userName">
<br>
<ng-container *ngIf="userName.errors?.required && (userName?.touched || userName?.dirty)">
<small class="formError">User Name is required!</small>
</ng-container>
<ng-container *ngIf="userName.errors?.minlength && (userName?.touched || userName?.dirty)">
<small class="formError">User Name must be at least 3 characters!</small>
</ng-container>
<br>
</form>
As you can see, userName.errors?.minlength
and userName.errors?.required
will directly reference the userName
in your component file. You also want to check for .touched
and .dirty
because you don't want to display the error only until your users have interacted with the form control.
For future reference, have a read on Angular official docs on reactive form validation (Note: read the first part only, the subsequent section in on template-driven forms)
You should also use <ng-container>
because *ngIf will render the elements in your DOM even if the *ngIf evaluates to false. See this Angular: The ng-container Element
component .ts file
import { Component } from '@angular/core';
import { FormBuilder, Validators, FormControl, FormGroup } from '@angular/forms';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
// requiredError: boolean; //not required
// minLengthError: boolean; //not required
form: FormGroup;
constructor(private fBuilder: FormBuilder) { }
get userName() { return this.form.get('userName'); } //create a userName variable for template to reference
ngOnInit(){
this.form = this.fBuilder.group({
userName: [
'', [ Validators.required, Validators.minLength(3)]
]
});
}
ngAfterViewChecked(){
console.log(this.userName.errors)
}
Forked Stackblitz Example ⚡⚡
Hope this helped!
来源:https://stackoverflow.com/questions/58550692/error-alerts-in-angular-reactive-forms-appear-one-step-late