Angular 表单总是返回无效?
Angular form is always returning invalid?
我有一个带有三个复选框的 Angular 响应式表单:
- 第一个复选框用于查看用户是否愿意保持匿名。如果用户未选中此选项,则名称字段将保留。如果选中,名称字段将被隐藏。
- 第二个复选框用于显示电子邮件字段。如果选中,则会出现电子邮件字段。如果它保持未选中状态,则不会出现电子邮件字段。
- 第三个复选框是 'Agree' 复选框。用户必须 select 这个才能提交表单。
我的问题是,即使满足所有这些要求,提交表单仍然无效。我想看看我的 submissionForm.valid
returns true
。这是我的提交表单组件:
编辑:感谢评论中的回答,我看到我所有的字段都返回 null
值。谁能帮我解决这个问题?我觉得我正在正确初始化所有内容,但我仍然对为什么值返回为 null
感到困惑。
import { Component, OnInit } from '@angular/core';
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
@Component({
selector: 'app-submission',
templateUrl: './submission-form.component.html',
styleUrls: [
'./submission-form.component.scss'
]
})
export class SubmissionFormComponent implements OnInit {
submissionForm: FormGroup;
formSubmitted = false; //Holds status of the form
private nameValidators = [
Validators.minLength(1),
Validators.maxLength(50)
];
private emailValidators = [
Validators.maxLength(250),
Validators.email
];
constructor(private fb: FormBuilder) { }
ngOnInit(): void {
this.createForm();
}
createForm(): void {
this.submissionForm = this.fb.group({
anonymous: [''],
name: ['', this.nameValidators],
contact: [''],
email: ['', this.emailValidators],
contentWarning: ['', [Validators.required]],
title: ['', Validators.required],
message: ['', [Validators.required, Validators.minLength(10), Validators.maxLength(10000)]],
agree: [false, [Validators.requiredTrue]]
});
}
onSubmit() {
if (this.submissionForm.get('anonymous').value == false) {
this.submissionForm.get('name').setValidators(this.nameValidators.concat(Validators.required));
} else {
this.submissionForm.get('name').setValidators(this.nameValidators);
}
if (this.submissionForm.get('contact').value == true) {
this.submissionForm.get('email').setValidators(this.emailValidators.concat(Validators.required));
} else {
this.submissionForm.get('email').setValidators(this.emailValidators);
}
this.formSubmitted = true; //Form has been submitted
console.log(this.submissionForm.valid);
if (this.submissionForm.valid) {
console.log('submissionForm', this.submissionForm.value); //Process the form
}
}
get anonymous() { return this.submissionForm.get('anonymous') };
get name() { return this.submissionForm.get('name') };
get contact() { return this.submissionForm.get('contact') };
get email() { return this.submissionForm.get('email') };
get contentWarning() { return this.submissionForm.get('contentWarning') };
get title() { return this.submissionForm.get('title') };
get message() { return this.submissionForm.get('message') };
get agree() { return this.submissionForm.get('agree') };
}
这是附带的模板代码:
<section class="section">
<div class="columns is-centered">
<div class="column is-four-fifths">
<h1 class="title">Submission Form</h1>
<p class="success" *ngIf="formSubmitted">Submitted successfully</p>
<form [formGroup]="submissionForm" (ngSubmit)="onSubmit()">
<div class="field">
<!--Checkbox for remaining anonymous-->
<label class="checkbox">
<input type="checkbox"
formControlName="anonymous">
Remain anonymous
</label>
</div>
<div class="field" [hidden]="anonymous.value">
<!--Checkbox for remaining anonymous-->
<input class="input"
type="text"
placeholder="Name"
formControlName="name"
[ngClass]="{'form-submitted': formSubmitted}">
<div class="help-is-danger" *ngIf="name.invalid && !name.pristine">Name should be 50 characters or less.</div>
</div>
<div class="field">
<!--Checkbox for being contacted-->
<label class="checkbox">
<input type="checkbox" formControlName="contact">
Contact me
</label>
</div>
<div class="field" [hidden]="!contact.value">
<!--Field for email-->
<input class="input"
type="text"
placeholder="Email"
formControlName="email"
[ngClass]="{'form-submitted': formSubmitted}">
<div class="help-is-danger" *ngIf="email.invalid && !email.pristine">
<p *ngIf="email.errors.email">Valid email is required.</p>
</div>
</div>
<div class="field">
<!--Field for content warnings-->
<input class="input"
type="text"
placeholder="Content warnings"
formControlName="name"
[ngClass]="{'form-submitted': formSubmitted}">
<div class="help-is-danger" *ngIf="contentWarning.invalid && !contentWarning.pristine">Content warnings are required.</div>
</div>
<div class="field">
<!--Field for title-->
<input class="input"
type="text"
placeholder="Title"
formControlName="title"
[ngClass]="{'form-submitted': formSubmitted}">
<div class="help-is-danger" *ngIf="title.invalid && !title.pristine">Title is required.</div>
</div>
<div class="field">
<!--Text area for message-->
<textarea class="textarea"
type="text"
placeholder="Your Submission"
formControlName="message">
</textarea>
<div class="help-is-danger" *ngIf="message.invalid && !message.pristine">
<p *ngIf="message.errors.minlength">Message is required and should be at least 10 characters.</p>
</div>
</div>
<div class="field">
<!--Checkbox for agree-->
<label class="checkbox">
<input type="checkbox"
formControlName="agree">
Agree
</label>
<div class="help-is-danger" *ngIf="submissionForm.hasError('required', 'agree') && !agree.pristine">Must check 'Agree' in order to submit.</div>
</div>
<!--Submit Button-->
<button type="submit" class="button is-danger is-outlined" [disabled]="!agree.valid && !submissionForm.valid">Submit</button>
</form>
</div>
</div>
</section>
我目前正在使用 Angular 9 和 Bulma 框架。
即使您隐藏了这些字段,表单仍然需要满足其验证器规则的值。
要在名称、电子邮件字段中禁用验证程序,您需要禁用反应式表单字段
if (this.submissionForm.get('anonymous').value == false) {
this.submissionForm.get('name').enable()
}
else{
this.submissionForm.get('name').disable();
}
如果以 rxjs 的方式使用 if else 会更好
this.submissionForm.get('anonymous').valueChanges.subscribe(
value => {
if (value == false) {
this.submissionForm.get('name').enable()
}
else{
this.submissionForm.get('name').disable();
}
}
)
因为您的字段只有在可见时才必须遵循一些规则,并且它们的可见状态取决于其他控件的值,所以您必须编写一些自定义验证程序。您有两种方法可以做到这一点:将自定义验证器直接应用于控件,或将它们应用于表单。两者都比较冗长,我会把我认为更容易理解的应用于表格的方法放在这里。
请务必注意,我只是在回答您对验证器的要求。这并不意味着这是实现您想要的用户体验的最佳或最简单的方法。
将这两个字段的验证器替换为应用于表单的自定义验证器:
createForm(): void {
this.submissionForm = this.fb.group(
{
...
name: [''], // <= no validators here
...
email: [''], // <= no validators here
...
},
// apply two custom validators to the form
{ validators: [validateName, validateEmail] }
);
}
编写您的自定义验证器:
function validateEmail(formGroup: FormGroup): ValidationErrors {
const anonymousCtrl = formGroup && formGroup.get('contact');
if (!anonymousCtrl || !anonymousCtrl.value) {return null;}
const control = formGroup.get('email');
const regex = /^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/;
const emailMalformed = !regex.test(control.value);
const emailRequired = !control.value;
let error = {};
if (emailMalformed) error = { ...error, emailMalformed: true };
if (emailRequired) error = { ...error, emailRequired: true };
return Object.keys(error).length ? error : null;
}
function validateName(formGroup: FormGroup): ValidationErrors {
const anonymousCtrl = formGroup && formGroup.get('anonymous');
if (!anonymousCtrl || anonymousCtrl.value) {return null;}
const control = formGroup.get('name');
const nameMaxLength = control.value && control.value.length > 250;
const nameRequired = !control.value;
let error = {};
if (nameMaxLength) error = { ...error, nameMaxLength: true };
if (nameRequired) error = { ...error, nameRequired: true };
return Object.keys(error).length ? error : null;
}
修复您的模板以在错误发生时在控件上显示错误
<input formControlName="name">
<div class="help-is-danger"
*ngIf="nameInvalid && !name.pristine">
Name should be 50 characters or less.
</div>
...
<input formControlName="email">
<div class="help-is-danger"
*ngIf="emailInvalid && !email.pristine">
A valid email is required.
</div>
编写 getter nameInvalid
和 emailInvalid
:
get nameInvalid() {
return (
this.submissionForm.getError('nameRequired') ||
this.submissionForm.getError('nameMaxLength')
);
}
get emailInvalid() {
return (
this.submissionForm.getError('emailRequired') ||
this.submissionForm.getError('emailMalformed')
);
}
我有一个带有三个复选框的 Angular 响应式表单:
- 第一个复选框用于查看用户是否愿意保持匿名。如果用户未选中此选项,则名称字段将保留。如果选中,名称字段将被隐藏。
- 第二个复选框用于显示电子邮件字段。如果选中,则会出现电子邮件字段。如果它保持未选中状态,则不会出现电子邮件字段。
- 第三个复选框是 'Agree' 复选框。用户必须 select 这个才能提交表单。
我的问题是,即使满足所有这些要求,提交表单仍然无效。我想看看我的 submissionForm.valid
returns true
。这是我的提交表单组件:
编辑:感谢评论中的回答,我看到我所有的字段都返回 null
值。谁能帮我解决这个问题?我觉得我正在正确初始化所有内容,但我仍然对为什么值返回为 null
感到困惑。
import { Component, OnInit } from '@angular/core';
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
@Component({
selector: 'app-submission',
templateUrl: './submission-form.component.html',
styleUrls: [
'./submission-form.component.scss'
]
})
export class SubmissionFormComponent implements OnInit {
submissionForm: FormGroup;
formSubmitted = false; //Holds status of the form
private nameValidators = [
Validators.minLength(1),
Validators.maxLength(50)
];
private emailValidators = [
Validators.maxLength(250),
Validators.email
];
constructor(private fb: FormBuilder) { }
ngOnInit(): void {
this.createForm();
}
createForm(): void {
this.submissionForm = this.fb.group({
anonymous: [''],
name: ['', this.nameValidators],
contact: [''],
email: ['', this.emailValidators],
contentWarning: ['', [Validators.required]],
title: ['', Validators.required],
message: ['', [Validators.required, Validators.minLength(10), Validators.maxLength(10000)]],
agree: [false, [Validators.requiredTrue]]
});
}
onSubmit() {
if (this.submissionForm.get('anonymous').value == false) {
this.submissionForm.get('name').setValidators(this.nameValidators.concat(Validators.required));
} else {
this.submissionForm.get('name').setValidators(this.nameValidators);
}
if (this.submissionForm.get('contact').value == true) {
this.submissionForm.get('email').setValidators(this.emailValidators.concat(Validators.required));
} else {
this.submissionForm.get('email').setValidators(this.emailValidators);
}
this.formSubmitted = true; //Form has been submitted
console.log(this.submissionForm.valid);
if (this.submissionForm.valid) {
console.log('submissionForm', this.submissionForm.value); //Process the form
}
}
get anonymous() { return this.submissionForm.get('anonymous') };
get name() { return this.submissionForm.get('name') };
get contact() { return this.submissionForm.get('contact') };
get email() { return this.submissionForm.get('email') };
get contentWarning() { return this.submissionForm.get('contentWarning') };
get title() { return this.submissionForm.get('title') };
get message() { return this.submissionForm.get('message') };
get agree() { return this.submissionForm.get('agree') };
}
这是附带的模板代码:
<section class="section">
<div class="columns is-centered">
<div class="column is-four-fifths">
<h1 class="title">Submission Form</h1>
<p class="success" *ngIf="formSubmitted">Submitted successfully</p>
<form [formGroup]="submissionForm" (ngSubmit)="onSubmit()">
<div class="field">
<!--Checkbox for remaining anonymous-->
<label class="checkbox">
<input type="checkbox"
formControlName="anonymous">
Remain anonymous
</label>
</div>
<div class="field" [hidden]="anonymous.value">
<!--Checkbox for remaining anonymous-->
<input class="input"
type="text"
placeholder="Name"
formControlName="name"
[ngClass]="{'form-submitted': formSubmitted}">
<div class="help-is-danger" *ngIf="name.invalid && !name.pristine">Name should be 50 characters or less.</div>
</div>
<div class="field">
<!--Checkbox for being contacted-->
<label class="checkbox">
<input type="checkbox" formControlName="contact">
Contact me
</label>
</div>
<div class="field" [hidden]="!contact.value">
<!--Field for email-->
<input class="input"
type="text"
placeholder="Email"
formControlName="email"
[ngClass]="{'form-submitted': formSubmitted}">
<div class="help-is-danger" *ngIf="email.invalid && !email.pristine">
<p *ngIf="email.errors.email">Valid email is required.</p>
</div>
</div>
<div class="field">
<!--Field for content warnings-->
<input class="input"
type="text"
placeholder="Content warnings"
formControlName="name"
[ngClass]="{'form-submitted': formSubmitted}">
<div class="help-is-danger" *ngIf="contentWarning.invalid && !contentWarning.pristine">Content warnings are required.</div>
</div>
<div class="field">
<!--Field for title-->
<input class="input"
type="text"
placeholder="Title"
formControlName="title"
[ngClass]="{'form-submitted': formSubmitted}">
<div class="help-is-danger" *ngIf="title.invalid && !title.pristine">Title is required.</div>
</div>
<div class="field">
<!--Text area for message-->
<textarea class="textarea"
type="text"
placeholder="Your Submission"
formControlName="message">
</textarea>
<div class="help-is-danger" *ngIf="message.invalid && !message.pristine">
<p *ngIf="message.errors.minlength">Message is required and should be at least 10 characters.</p>
</div>
</div>
<div class="field">
<!--Checkbox for agree-->
<label class="checkbox">
<input type="checkbox"
formControlName="agree">
Agree
</label>
<div class="help-is-danger" *ngIf="submissionForm.hasError('required', 'agree') && !agree.pristine">Must check 'Agree' in order to submit.</div>
</div>
<!--Submit Button-->
<button type="submit" class="button is-danger is-outlined" [disabled]="!agree.valid && !submissionForm.valid">Submit</button>
</form>
</div>
</div>
</section>
我目前正在使用 Angular 9 和 Bulma 框架。
即使您隐藏了这些字段,表单仍然需要满足其验证器规则的值。
要在名称、电子邮件字段中禁用验证程序,您需要禁用反应式表单字段
if (this.submissionForm.get('anonymous').value == false) {
this.submissionForm.get('name').enable()
}
else{
this.submissionForm.get('name').disable();
}
如果以 rxjs 的方式使用 if else 会更好
this.submissionForm.get('anonymous').valueChanges.subscribe(
value => {
if (value == false) {
this.submissionForm.get('name').enable()
}
else{
this.submissionForm.get('name').disable();
}
}
)
因为您的字段只有在可见时才必须遵循一些规则,并且它们的可见状态取决于其他控件的值,所以您必须编写一些自定义验证程序。您有两种方法可以做到这一点:将自定义验证器直接应用于控件,或将它们应用于表单。两者都比较冗长,我会把我认为更容易理解的应用于表格的方法放在这里。
请务必注意,我只是在回答您对验证器的要求。这并不意味着这是实现您想要的用户体验的最佳或最简单的方法。
将这两个字段的验证器替换为应用于表单的自定义验证器:
createForm(): void {
this.submissionForm = this.fb.group(
{
...
name: [''], // <= no validators here
...
email: [''], // <= no validators here
...
},
// apply two custom validators to the form
{ validators: [validateName, validateEmail] }
);
}
编写您的自定义验证器:
function validateEmail(formGroup: FormGroup): ValidationErrors {
const anonymousCtrl = formGroup && formGroup.get('contact');
if (!anonymousCtrl || !anonymousCtrl.value) {return null;}
const control = formGroup.get('email');
const regex = /^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/;
const emailMalformed = !regex.test(control.value);
const emailRequired = !control.value;
let error = {};
if (emailMalformed) error = { ...error, emailMalformed: true };
if (emailRequired) error = { ...error, emailRequired: true };
return Object.keys(error).length ? error : null;
}
function validateName(formGroup: FormGroup): ValidationErrors {
const anonymousCtrl = formGroup && formGroup.get('anonymous');
if (!anonymousCtrl || anonymousCtrl.value) {return null;}
const control = formGroup.get('name');
const nameMaxLength = control.value && control.value.length > 250;
const nameRequired = !control.value;
let error = {};
if (nameMaxLength) error = { ...error, nameMaxLength: true };
if (nameRequired) error = { ...error, nameRequired: true };
return Object.keys(error).length ? error : null;
}
修复您的模板以在错误发生时在控件上显示错误
<input formControlName="name">
<div class="help-is-danger"
*ngIf="nameInvalid && !name.pristine">
Name should be 50 characters or less.
</div>
...
<input formControlName="email">
<div class="help-is-danger"
*ngIf="emailInvalid && !email.pristine">
A valid email is required.
</div>
编写 getter nameInvalid
和 emailInvalid
:
get nameInvalid() {
return (
this.submissionForm.getError('nameRequired') ||
this.submissionForm.getError('nameMaxLength')
);
}
get emailInvalid() {
return (
this.submissionForm.getError('emailRequired') ||
this.submissionForm.getError('emailMalformed')
);
}