【问题标题】:mat-error is not showing for custom Form Control自定义表单控件未显示 mat-error
【发布时间】:2020-01-31 00:15:00
【问题描述】:

创建地址自定义表单控制器并在 CustomerForm 内部使用,在不填写任何内容的情况下提交表单时,客户代码和客户名称的必填字段出现错误,地址 1、国家、城市中的必填字段未出现任何错误,和邮政编码。

我曾尝试添加显示地址组合错误的自定义验证器。但它并没有解决我的问题,因为它总是显示错误地址,直到地址的所有必填字段都被填满。

customer.component.html

<form (ngSubmit)=" custForm.valid && saveOrUpdateCustomer()" [formGroup]="custForm">

    <!-- Customer Code -->
     <mat-form-field>
        <input matInput placeholder="Customer Code" formControlName="custCode" required >
        <mat-error *ngIf="custForm.get('custCode').hasError('required')">
            *Required field
        </mat-error>
    </mat-form-field>

    <!-- Customer Name -->
    <mat-form-field>
        <input matInput placeholder="Customer Name" formControlName="custName" required>
        <mat-error *ngIf="custForm.get('custName').hasError('required')">
            *Required field
        </mat-error>
    </mat-form-field>

    <h6>Address Details</h6>
    <!--Address Detail-->
    <app-address formControlName="address" #address></app-address>
    <div *ngIf="custForm.controls['address'].hasError('isNotValid')">
    </div>
    <div>
        <button type="submit" >Submit</button>
    </div>
</form>

address.component.hml

<form [formGroup]="addressForm">

  <!-- Address 1 -->
  <mat-form-field>
    <input matInput placeholder="Address 1" formControlName="address1" required>
    <mat-error *ngIf="addressForm.get('address1').hasError('required')">
      *Required field
    </mat-error>
  </mat-form-field>
`
  <!-- Address 2 -->
  <mat-form-field>
    <input matInput placeholder="Address 2" formControlName="address2">
  </mat-form-field>

  <!-- Address 3 -->
  <mat-form-field>
    <input matInput placeholder="Address 3" formControlName="address3">
  </mat-form-field>

    <!-- Country -->
    <mat-form-field>
    <input matInput placeholder="Country" formControlName="country" required>
    <mat-error *ngIf="addressForm.get('country').hasError('required')">
      *Required field
    </mat-error>
  </mat-form-field>

    <!-- State -->
    <mat-form-field>
    <input matInput placeholder="State" formControlName="state">
  </mat-form-field>

    <!-- City -->
    <mat-form-field>
    <input matInput placeholder="City" formControlName="city" required>
    <mat-error *ngIf="addressForm.get('country').hasError('required')">
      *Required field
    </mat-error>
  </mat-form-field>

  <!-- Zip Code -->
  <mat-form-field>
    <input matInput placeholder="Zip Code">
    <mat-error *ngIf="addressForm.get('country').hasError('required')">
      *Required field
    </mat-error>
  </mat-form-field>

</form>

address.component.ts

import { Component, OnInit, forwardRef} from '@angular/core';
import { FormGroup, FormBuilder, Validators, ControlValueAccessor, NG_VALUE_ACCESSOR, FormControl, NG_VALIDATORS } from '@angular/forms';
import { Address } from 'app/model/address/address.interface';

@Component({
  selector: 'app-address',
  templateUrl: './address.component.html',
  styleUrls: ['./address.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: AddressComponent,
      multi: true
    },
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => AddressComponent),
      multi: true,
    }
  ]
})
export class AddressComponent implements OnInit, ControlValueAccessor, Validators {

  public addressForm: FormGroup;
  private _address: any = {};
  istoched = false;

  constructor(
    private fb: FormBuilder,
  ) { }

  ngOnInit() {
    this.addressForm = this.fb.group({
      address1: ['', [Validators.required]],
      address2: [''],
      address3: [''],
      country: ['', [Validators.required]],
      state: [''],
      city: ['', [Validators.required]],
      zip: [''],
    });
  }

  propagateChange = (_: any) => { };

  writeValue(address: Address) {
    this._address = address;
    if (address) {
      this.addressForm.setValue({
        address1: address.address1,
        address2: address.address2,
        address3: address.address3,
        country: address.country,
        state: address.state,
        city: address.city,
        zip: address.zip,
      });
    }
  }

  registerOnChange(fn: any) {
    this.addressForm.valueChanges.subscribe(() => {
      fn(this.addressForm.value);
    });
  }

  registerOnTouched(fn: any) {
    this.propagateChange = fn;
  }

  public validate(formControl: FormControl) {
    let isValid = true;
    Object.keys(this.addressForm.controls).forEach((control) => {
      if (this.addressForm.controls[control] instanceof FormControl) {
        if (!(<FormControl>this.addressForm.controls[control]).valid) {
          isValid = false;
          if (this.istoched) {
            this.addressForm.controls[control].markAsTouched();
          }
        }
      }
    });
    this.istoched = true;
    if (isValid) {
      return null;
    } else {
      return {
        primaryContactError: {
          valid: false,
        }
      };
    }
  }
}

点击客户表单的提交按钮时,如果任何必填字段未填写,则应显示必填错误。

【问题讨论】:

标签: angular forms angular-material2 angular-reactive-forms


【解决方案1】:

我想你可能误解了如何使用ControlValueAccessor。它与FormControl 对象结合使用,以便以编程方式对FormControl 进行的更改传递到组件的视图。您已经在AddressComponent 中实现了ControlValueAccessor,因此需要使用&lt;app-address [formControl]="..."&gt;&lt;app-address formControlName="..."&gt;,但似乎AddressComponent 代表FormGroup。因此,您的任何ControlValueAccessor 实现都不会按预期工作 - 您可能根本不需要它。

您的validate() 逻辑要求表单控件无效才能将其标记为已触摸,但如果未触摸该控件,它将始终有效(默认行为)。要更改默认行为,请实现您自己的 ErrorStateMatcher 并将其应用于您的 MatInput 输入。我没有看到任何注册或调用 validate() 的内容 - 您也需要这样做。

此外,没有必要对 &lt;mat-error&gt; 元素使用 *ngIf 条件,除非您有多个 &lt;mat-error&gt;s 用于不同的错误类型,或者您想隐藏其他错误类型的错误。 &lt;mat-error&gt;只会在表单控件状态无效时显示。

【讨论】:

  • 我应该使用 FormArray 并在客户表单中注入地址表单还是应该使用 2 种不同的表单 1 用于客户和 1 用于地址?
  • 我想让地址来自字段通用。有可能吗?
  • 如果不知道自己想要达到什么目标,就很难知道如何回答。
  • 我只是想让地址通用,因为它几乎用于所有形式,以避免项目中的代码精致。
  • 您应该发布另一个问题并提供详细信息。这与 mat-error 无关。
猜你喜欢
  • 1970-01-01
  • 2021-12-18
  • 2018-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多