Angular 表单总是返回无效?

Angular form is always returning invalid?

我有一个带有三个复选框的 Angular 响应式表单:

  1. 第一个复选框用于查看用户是否愿意保持匿名。如果用户未选中此选项,则名称字段将保留。如果选中,名称字段将被隐藏。
  2. 第二个复选框用于显示电子邮件字段。如果选中,则会出现电子邮件字段。如果它保持未选中状态,则不会出现电子邮件字段。
  3. 第三个复选框是 '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();
    }
  }
)

因为您的字段只有在可见时才必须遵循一些规则,并且它们的可见状态取决于其他控件的值,所以您必须编写一些自定义验证程序。您有两种方法可以做到这一点:将自定义验证器直接应用于控件,或将它们应用于表单。两者都比较冗长,我会把我认为更容易理解的应用于表格的方法放在这里。

Stackblitz demo

请务必注意,我只是在回答您对验证器的要求。这并不意味着这是实现您想要的用户体验的最佳或最简单的方法。

将这两个字段的验证器替换为应用于表单的自定义验证器:

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 nameInvalidemailInvalid:

get nameInvalid() {
  return (
    this.submissionForm.getError('nameRequired') ||
    this.submissionForm.getError('nameMaxLength')
  );
}

get emailInvalid() {
  return (
    this.submissionForm.getError('emailRequired') ||
    this.submissionForm.getError('emailMalformed')
  );
}