Angular 来自 api 响应的反应式自定义异步验证
Angular reactive form custom async validation from api response
我是 Angular (V6) 的新手,我有点迷路了。我一直在寻找问题的答案,但经过几天的研究后,我找不到我要找的东西。希望大家帮帮我。
我开发了一个 API,其中有一个用于帐户注册的端点。在 POST 请求中,服务器只检查正文请求中的所有内容是否正常,否则它会发送一个 JSON 字段错误(例如用户名已被占用或太短,密码不匹配,电子邮件地址已经采取等)。
我想在提交时在反应式表单中使用异步验证,这样我就可以获得服务器响应并在表单中显示错误。但我没能做到。对于这种情况,Angular 文档中没有真正明确的示例。
sign-up.component.ts
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { ApiService } from './../../services/api.service';
@Component({
selector: 'app-sign-up',
templateUrl: './sign-up.component.html',
styleUrls: ['./sign-up.component.scss']
})
export class SignUpComponent implements OnInit {
signupForm: FormGroup;
constructor(
private formBuilder: FormBuilder,
private apiService: ApiService
) { }
ngOnInit() {
this.initForm();
}
initForm() {
this.signupForm = this.formBuilder.group({
firstname: ['', Validators.required],
lastname: ['', Validators.required],
username: ['', Validators.required],
email: ['', Validators.required],
password: ['', Validators.required],
confirmPassword: ['', Validators.required],
birthdate: ['', Validators.required],
gender: ['', Validators.required]
}, {
asyncValidator: this.myAsyncValidator() // Am I wrong?
});
}
onSubmitForm() {
console.log('submit');
// On submit, I send the form to the server.
// If the account has been created, I redirect the user (no problem for that)
// but if there are some errors, I would like to display them into the form.
}
myAsyncValidator () {
// I'm lost here
}
}
api.service.ts
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
const apiURL = 'MY_SERVER_URL';
@Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(private http: HttpClient) { }
signUp(data: Object) {
const body = JSON.stringify(data);
return this.http.post(`${apiURL}/authentication/signup/`, body, httpOptions);
}
}
sign-up.component.html
<div class="signup-container">
<div class="mx-auto">
<form [formGroup]="signupForm" (ngSubmit)="onSubmitForm()">
<mat-form-field>
<input matInput placeholder="Prénom" formControlName="firstname">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Nom" formControlName="lastname">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Pseudo" formControlName="username">
</mat-form-field>
<mat-form-field>
<input matInput type="email" placeholder="Email" formControlName="email">
</mat-form-field>
<mat-form-field>
<input matInput type="password" placeholder="Mot de passe" formControlName="password">
</mat-form-field>
<mat-form-field>
<input matInput type="password" placeholder="Confirmer le mot de passe" formControlName="confirmPassword">
</mat-form-field>
<mat-form-field>
<input matInput [matDatepicker]="picker" placeholder="Date de naissance" formControlName="birthdate" (focus)="picker.open()">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
<mat-radio-group formControlName="gender">
<mat-radio-button value="Femme">Femme</mat-radio-button>
<mat-radio-button value="Homme">Homme</mat-radio-button>
</mat-radio-group>
<button type="submit" mat-stroked-button class="save-button"><i class="material-icons save-icon">save</i>Inscription</button>
</form>
</div>
</div>
我找到了解决问题的方法。我已经从反应形式切换到模板驱动形式。我知道这不是最好的解决方案,我会尝试找到更好的方法来解决这个问题。希望这可以帮助任何人!
这是我所做的:
sign-up.component.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { NgForm } from '@angular/forms';
import { ApiService } from './../../services/api.service';
@Component({
selector: 'app-sign-up',
templateUrl: './sign-up.component.html',
styleUrls: ['./sign-up.component.scss']
})
export class SignUpComponent implements OnInit {
isSending = false;
submitText = 'Inscription';
constructor(
private apiService: ApiService,
private router: Router
) { }
ngOnInit() { }
onSubmit(formData: NgForm) {
this.isSending = true;
this.submitText = 'Inscription en cours...';
this.apiService.signUp(formData.value).subscribe(
response => {
this.router.navigate(['/signin']);
},
error => {
const errors = error.error.message.errors; // Yeah... Mongoose validator...
for (const fieldError in errors) {
if (errors[fieldError]) {
formData.form.controls[fieldError].setErrors({ invalid: errors[fieldError]['message'] });
}
}
this.isSending = false;
this.submitText = 'Inscription';
}
);
}
}
sign-up.component.html
<div class="signup-container">
<div class="mx-auto">
<form (ngSubmit)="onSubmit(f)" #f="ngForm">
<mat-form-field>
<input matInput placeholder="Prénom" ngModel name="firstname" #firstname="ngModel">
<mat-error *ngIf="firstname.errors">{{firstname.errors['invalid']}}</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Nom" ngModel name="lastname" #lastname="ngModel">
<mat-error *ngIf="lastname.errors">{{lastname.errors['invalid']}}</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Pseudo" ngModel name="username" #username="ngModel">
<mat-error *ngIf="username.errors">{{username.errors['invalid']}}</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput type="email" placeholder="Email" ngModel name="email" #email="ngModel">
<mat-error *ngIf="email.errors">{{email.errors['invalid']}}</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput type="password" placeholder="Mot de passe" ngModel name="password" #password="ngModel">
<mat-error *ngIf="password.errors">{{password.errors['invalid']}}</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput type="password" placeholder="Confirmer le mot de passe" ngModel name="confirmPassword" #confirmPassword="ngModel">
<mat-error *ngIf="confirmPassword.errors">{{confirmPassword.errors['invalid']}}</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput [matDatepicker]="picker" placeholder="Date de naissance" ngModel name="birthdate" #birthdate="ngModel" (focus)="picker.open()">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
<mat-error *ngIf="birthdate.errors">{{birthdate.errors['invalid']}}</mat-error>
</mat-form-field>
<mat-radio-group ngModel name="gender" #gender="ngModel">
<mat-radio-button value="Femme">Femme</mat-radio-button>
<mat-radio-button value="Homme">Homme</mat-radio-button>
<mat-error *ngIf="gender.errors">{{gender.errors['invalid']}}</mat-error>
</mat-radio-group>
<button type="submit" mat-stroked-button class="save-button" [disabled]="isSending">
<i class="material-icons save-icon">save</i>{{submitText}}
</button>
</form>
</div>
</div>
我是 Angular (V6) 的新手,我有点迷路了。我一直在寻找问题的答案,但经过几天的研究后,我找不到我要找的东西。希望大家帮帮我。
我开发了一个 API,其中有一个用于帐户注册的端点。在 POST 请求中,服务器只检查正文请求中的所有内容是否正常,否则它会发送一个 JSON 字段错误(例如用户名已被占用或太短,密码不匹配,电子邮件地址已经采取等)。
我想在提交时在反应式表单中使用异步验证,这样我就可以获得服务器响应并在表单中显示错误。但我没能做到。对于这种情况,Angular 文档中没有真正明确的示例。
sign-up.component.ts
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { ApiService } from './../../services/api.service';
@Component({
selector: 'app-sign-up',
templateUrl: './sign-up.component.html',
styleUrls: ['./sign-up.component.scss']
})
export class SignUpComponent implements OnInit {
signupForm: FormGroup;
constructor(
private formBuilder: FormBuilder,
private apiService: ApiService
) { }
ngOnInit() {
this.initForm();
}
initForm() {
this.signupForm = this.formBuilder.group({
firstname: ['', Validators.required],
lastname: ['', Validators.required],
username: ['', Validators.required],
email: ['', Validators.required],
password: ['', Validators.required],
confirmPassword: ['', Validators.required],
birthdate: ['', Validators.required],
gender: ['', Validators.required]
}, {
asyncValidator: this.myAsyncValidator() // Am I wrong?
});
}
onSubmitForm() {
console.log('submit');
// On submit, I send the form to the server.
// If the account has been created, I redirect the user (no problem for that)
// but if there are some errors, I would like to display them into the form.
}
myAsyncValidator () {
// I'm lost here
}
}
api.service.ts
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
const apiURL = 'MY_SERVER_URL';
@Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(private http: HttpClient) { }
signUp(data: Object) {
const body = JSON.stringify(data);
return this.http.post(`${apiURL}/authentication/signup/`, body, httpOptions);
}
}
sign-up.component.html
<div class="signup-container">
<div class="mx-auto">
<form [formGroup]="signupForm" (ngSubmit)="onSubmitForm()">
<mat-form-field>
<input matInput placeholder="Prénom" formControlName="firstname">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Nom" formControlName="lastname">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Pseudo" formControlName="username">
</mat-form-field>
<mat-form-field>
<input matInput type="email" placeholder="Email" formControlName="email">
</mat-form-field>
<mat-form-field>
<input matInput type="password" placeholder="Mot de passe" formControlName="password">
</mat-form-field>
<mat-form-field>
<input matInput type="password" placeholder="Confirmer le mot de passe" formControlName="confirmPassword">
</mat-form-field>
<mat-form-field>
<input matInput [matDatepicker]="picker" placeholder="Date de naissance" formControlName="birthdate" (focus)="picker.open()">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
<mat-radio-group formControlName="gender">
<mat-radio-button value="Femme">Femme</mat-radio-button>
<mat-radio-button value="Homme">Homme</mat-radio-button>
</mat-radio-group>
<button type="submit" mat-stroked-button class="save-button"><i class="material-icons save-icon">save</i>Inscription</button>
</form>
</div>
</div>
我找到了解决问题的方法。我已经从反应形式切换到模板驱动形式。我知道这不是最好的解决方案,我会尝试找到更好的方法来解决这个问题。希望这可以帮助任何人!
这是我所做的:
sign-up.component.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { NgForm } from '@angular/forms';
import { ApiService } from './../../services/api.service';
@Component({
selector: 'app-sign-up',
templateUrl: './sign-up.component.html',
styleUrls: ['./sign-up.component.scss']
})
export class SignUpComponent implements OnInit {
isSending = false;
submitText = 'Inscription';
constructor(
private apiService: ApiService,
private router: Router
) { }
ngOnInit() { }
onSubmit(formData: NgForm) {
this.isSending = true;
this.submitText = 'Inscription en cours...';
this.apiService.signUp(formData.value).subscribe(
response => {
this.router.navigate(['/signin']);
},
error => {
const errors = error.error.message.errors; // Yeah... Mongoose validator...
for (const fieldError in errors) {
if (errors[fieldError]) {
formData.form.controls[fieldError].setErrors({ invalid: errors[fieldError]['message'] });
}
}
this.isSending = false;
this.submitText = 'Inscription';
}
);
}
}
sign-up.component.html
<div class="signup-container">
<div class="mx-auto">
<form (ngSubmit)="onSubmit(f)" #f="ngForm">
<mat-form-field>
<input matInput placeholder="Prénom" ngModel name="firstname" #firstname="ngModel">
<mat-error *ngIf="firstname.errors">{{firstname.errors['invalid']}}</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Nom" ngModel name="lastname" #lastname="ngModel">
<mat-error *ngIf="lastname.errors">{{lastname.errors['invalid']}}</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Pseudo" ngModel name="username" #username="ngModel">
<mat-error *ngIf="username.errors">{{username.errors['invalid']}}</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput type="email" placeholder="Email" ngModel name="email" #email="ngModel">
<mat-error *ngIf="email.errors">{{email.errors['invalid']}}</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput type="password" placeholder="Mot de passe" ngModel name="password" #password="ngModel">
<mat-error *ngIf="password.errors">{{password.errors['invalid']}}</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput type="password" placeholder="Confirmer le mot de passe" ngModel name="confirmPassword" #confirmPassword="ngModel">
<mat-error *ngIf="confirmPassword.errors">{{confirmPassword.errors['invalid']}}</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput [matDatepicker]="picker" placeholder="Date de naissance" ngModel name="birthdate" #birthdate="ngModel" (focus)="picker.open()">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
<mat-error *ngIf="birthdate.errors">{{birthdate.errors['invalid']}}</mat-error>
</mat-form-field>
<mat-radio-group ngModel name="gender" #gender="ngModel">
<mat-radio-button value="Femme">Femme</mat-radio-button>
<mat-radio-button value="Homme">Homme</mat-radio-button>
<mat-error *ngIf="gender.errors">{{gender.errors['invalid']}}</mat-error>
</mat-radio-group>
<button type="submit" mat-stroked-button class="save-button" [disabled]="isSending">
<i class="material-icons save-icon">save</i>{{submitText}}
</button>
</form>
</div>
</div>