【问题标题】:Angular 4 Reactive Forms Toggle Validation for Hidden Form ElementsAngular 4 响应式表单切换隐藏表单元素的验证
【发布时间】:2018-08-28 13:02:58
【问题描述】:

我有一个响应式表单,加载时不需要任何字段。如果选择了将其他表单元素添加到 formGroup 的选项,则新显示的字段将是所有必需的。 如果昵称字段被隐藏,那么您应该能够提交表单就好了。如果显示昵称,则需要昵称字段并且禁用提交按钮,直到昵称字段已满。 这是我想做的一个示例。

我的问题是,如何在表单元素显示/隐藏后启用/禁用验证?

App.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { HelloComponent } from './hello.component';

@NgModule({
  imports:      [ BrowserModule, FormsModule, ReactiveFormsModule ],
  declarations: [ AppComponent, HelloComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

App.component.ts

import { Component, OnInit } from '@angular/core';
import { Validators, FormControl, FormGroup, FormBuilder } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit  {
  name = 'My Reactive Form';

  constructor(
    private fb: FormBuilder
  ) {}

  myForm: FormGroup;
  showNick: boolean = false;

  ngOnInit() {
    this.myForm = this.fb.group({
      'firstName': new FormControl(),
      'nickName': new FormControl('', Validators.required),
      'lastName': new FormControl()
    })
  }

  toggleNick() {
    this.showNick = !this.showNick;
  }
}

app.component.html

<form [formGroup]="myForm">
  <div class="my-box">
    <label>
      First Name
      <input type="text" formControlName="firstName">
    </label>
  </div>
  <div class="my-box nickname">
    Nickname? &nbsp; <a (click)="toggleNick()">yes / no</a>
  </div>
  <div class="my-box" *ngIf="showNick">
    <label>
      Nickname
      <input type="text" formControlName="nickName">
      <span class="validation-message" *ngIf="!myForm.controls['nickName'].valid && myForm.controls['nickName'].dirty">
    This field is invalid
  </span>
    </label>
  </div>
  <div class="my-box">
    <label>
      Last Name
      <input type="text" formControlName="lastName">
    </label>
  </div>
  <button [disabled]="myForm.invalid">Submit</button>
</form>

【问题讨论】:

  • 您似乎还没有编写代码来执行您所描述的操作。有什么阻止你?你的问题在哪里?
  • 我无法完成我需要的工作。我将在顶部编辑我的问题。基本上,如果元素被隐藏,那么 formGroup 中仍然需要一些东西。隐藏表单元素后如何切换验证?
  • 这是我的工作示例以及 DeborahK 提供的解决方案:stackblitz.com/edit/angular-twjirf

标签: angular forms validation reactive


【解决方案1】:

如果您有很多复杂表单中的可选字段,我认为这些解决方案不可行。

我所做的就是这样:

export class MyComponent implements AfterViewChecked {

 @ViewChildren(FormControlName) allFormControlInDOM: QueryList<FormControlName>;

 // ...

  ngAfterViewChecked(): void {
    // We must disable the controls that are not in the DOM
    // If we don't, they are used for validating the form
    if (this.allFormControlInDOM) {
      const controls: { [p: string]: AbstractControl } = this.myForm.controls;
      for (const control in controls) {
        if (controls.hasOwnProperty(control) && controls[control] instanceof FormControl) {
          const found = this.allFormControlInDOM.find((item: FormControlName) => item.name === control);
          if (found) {
            controls[control].enable();
          } else {
            controls[control].disable();
            controls[control].setValue(null);
          }
        }
      }
    }
  }

// ...

}

希望能帮到你:)

【讨论】:

    【解决方案2】:

    即使字段对用户隐藏,字段在响应式中也是活动的。因此,您只需使用以下代码从响应式中禁用该字段

    this.myForm.get("nickName").disable();
    

    如下所示更改函数 toggleNick()

    toggleNick() {
        this.showNick = !this.showNick;
        if(showNick) {
             this.myForm.get("nickName").enable();
        } else {
             this.myForm.get("nickName").disable();
        }
    }
    

    【讨论】:

    • 这应该是例外的答案。我没有意识到禁用从表单值中提取控制值。我花了几个小时试图从隐藏的表单中删除控件,然后将其添加回显示的父组,但是字段模板将失去它与父组中的值的连接。因此,在将表单值删除然后添加回表单后,它不会更新表单值。谢谢!!!!!!!
    • 这也是唯一对我有用的选项。我有一个动态的 formArray,每个元素都有验证器。虽然清除验证器调用有效。我不能使用 set 验证器,因为 formArray 不存在这样的函数。
    【解决方案3】:

    我认为这样做的最佳选择是将字段作为初始表单的一部分并进行所有验证,并且当您希望以编程方式禁用和启用字段或嵌套表单时。

    示例:https://stackblitz.com/edit/angular-ojebff

    https://stackblitz.com/edit/angular-ojebff?embed=1&file=app/input-errors-example.html

    【讨论】:

    • 示例中的 detectChanges() 不是必需的。
    【解决方案4】:

    在我的申请中,我有类似的要求。如果用户要求通过文本通知,则需要电话。否则电话号码是可选的。

    我写了这个方法:

    setNotification(notifyVia: string): void {
        const phoneControl = this.customerForm.get('phone');
        if (notifyVia === 'text') {
            phoneControl.setValidators(Validators.required);
        } else {
            phoneControl.clearValidators();
        }
        phoneControl.updateValueAndValidity();
    }
    

    从 ngOnInit 中的这段代码调用它:

        this.customerForm.get('notification').valueChanges
                         .subscribe(value => this.setNotification(value));
    

    如果用户更改通知字段(这是一个单选按钮),它会调用setNotification 方法传入值。如果值为“文本”通知,则将手机的验证设置为必需。

    否则它会清除电话字段的验证。

    然后它必须调用updateValueAndValidity 来使用这个新的验证更新表单信息。

    【讨论】:

    • 感谢这么好的回答。存在一个问题,我在选定用户的基础上使用了一些常见字段和一些差异字段(隐藏/显示),我如何清除或重置表单以及您的答案。谢谢
    • 我在 Deborah 的复数课程中看到你这样做了,但已经完全隔开。很高兴你也在这里发帖。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    • 2019-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-08
    相关资源
    最近更新 更多