想要添加带有角反应形式(嵌套形式)的自定义验证

时间:2020-07-23 03:13:09

标签: angular rxjs angular-reactive-forms reactive-forms ng-zorro-antd

我正在使用Angular Reactive表单,并且有如下数据:

detail = {
          customerNo: '1231234123412',
          nameKanji: 'test0',
          nameKana: 'かな',
          gender: '0',
          birthday: '2019-02-02',
          telNo: '79-0999-1111',
          addressZip: '120-0813',
          address: 'takenotsuka0',
          email: 'slack@test.com',
          point: '1231230',
          family: [
            { name: 'テスト0', relationship: "wife" },
            { name: 'テスト1', relationship: "brother" }
          ]
        };

我已经如下定义了我的formGroup

 this.detailForm = this.fb.group(
      {
        global: [null],
        customerNo: [
          null,
          [Validators.required, StringValidator.equalLength(13)]
        ],
        nameKanji: [null, [Validators.required, Validators.maxLength(60)]],
        nameKana: [null, [Validators.required, Validators.maxLength(60)]],
        gender: ['1', [Validators.required]],
        birthday: [null, [Validators.required]],
        addressZip: [null, []],
        telNo: [null, [Validators.required, StringValidator.equalLength(13)]],
        address: [null],
        email: [null, [Validators.email, Validators.maxLength(100)]],
        point: [null, [Validators.required]],
        family: this.fb.array([])
      },
      { updateOn: 'blur' }
    );

 public addFamilyGroup() {
    return this.fb.group({
      name: [null, [Validators.required]],
      relationship: [null, [Validators.required]]
    });
  }

  get familyArray(): FormArray {
    return <FormArray>this.detailForm.get('family');
  }

您可以看到family是数据结构中的嵌套数组,现在,如果我已经具有family.wife值,我想为其添加自定义验证。

检查下面的图像。如果我已经选择了妻子,下次如果再次选择了妻子,则需要显示错误确认消息。 非常感谢。 enter image description here

2 个答案:

答案 0 :(得分:1)

您需要在formArray上创建一个自定义的表单验证器,在this SO的最后一部分中有一个示例

创建表单数组的customFormArray总是一样的(我喜欢将自定义表单错误作为函数放入组件本身中

  allDifferent()
  {
    return (formArray:FormArray)=>{
      //we create an array only with the "relationShip"
      const value=formArray.value?formArray.value.map(x=>x.relationship):null

      //(value.filter(...) create an array with the repeat values
      //so, we check the length to return null or an object
      return value && 
            (value.filter((item, index) => item && value.indexOf(item) != index)).length>0?
            {error:"values repeat"}:null
    }
  }

当您创建表单时

family: this.fb.array([],this.allDifferent())

更新,如果我们想要重复索引,我们可以创建一个自定义formError,该错误返回带有重复索引的数组“错误”,请记住,我们选择了想要返回的内容

  allDifferentIndex()
  {
    return (formArray:FormArray)=>{
      //we create an array of objects [{relationship:..,index:..},{relationship:..,index:...}..]
      const value=formArray.value?formArray.value
           .map((x,index)=>({relationship:x.relationship,index:index})):null

      if (value)
      {
        //we filter the array only with the values repeats           
        const repeatValues=(value.filter((item, index) => item.relationship && 
               value.findIndex(f=>f.relationship==item.relationship) != index))

       //if the array length>0 return an object {error:value}
       // the value is an array of numbers, we use map to return only the "index"
        return repeatValues.length?{error:repeatValues.map(x=>x.index)}:null
      }
    }
  }

我们可以使用类似

<div formArrayName="family">
    <div *ngFor="let group of familyArray.controls;let i=index" [formGroupName]="i">
        <input formControlName="relationship">
        <span *ngIf="familyArray.errors?.error && familyArray.errors.error.indexOf(i)>=0">
            *
        </span>
    </div>
</div>

并使用

family: this.fb.array([],this.allDifferentIndex())

更新2 ,我们可以创建一个更通用的customError,只是一个函数

  export function allDifferentIndex(property:string)
  {
    return (formArray:FormArray)=>{
      const value=formArray.value?formArray.value.map((x,index)=>({field:x[property],index:index})):null
      if (value)
      {
        const repeatValues=(value.filter((item, index) => item.field && value.findIndex(f=>f.field==item.field) != index))
        return repeatValues.length?{error:repeatValues.map(x=>x.index)}:null
      }
    }
  }

并用作

family: this.fb.array([],allDifferentIndex('relationship'))

一个简单的stackblitz using inputs-我使用了最后一个方法,一个外部函数,但是我在代码中留给了其他人

答案 1 :(得分:0)

非常感谢。 我尝试下面的代码。当我两次选择“妻子选项”时,浏览器现在显示错误消息。但是当我将选择框更改为另一个选项时,错误消息不会消失。

以html

<small id="fnHelp" *ngIf="familyArray.invalid && familyArray.touched" class="form-text custom-invalid">error</small>

在component.ts

 public checkValidator = (c: AbstractControl) => {
    const getValue = this.detailForm.value.family;
    if (Object.keys(getValue).length >= 2) {
      console.log('valid', this.familyArray);
      return { error: '配偶者を二人以上登録することができません' };
    }
    return null;
  };

和familyArray.errors的控制台似乎总是错误的

第一次单击。错误消息显示: enter image description here

当我更改第二个框时。错误消息不会消失: enter image description here

也。该消息会显示在所有选择框中,而不是我选择的那个框中。

我已将所有代码粘贴到代码段中

import { differenceInCalendarDays, format } from 'date-fns';
import { NzMessageService } from 'ng-zorro-antd';
import { untilDestroyed } from 'ngx-take-until-destroy';
import { filter } from 'rxjs/operators';
import { CustomerDetailQuery } from 'src/business/queries/customer-detail.query';
import { CustomerDetailService } from 'src/business/services/customer-detail.service';
import { CustomerService } from 'src/business/services/customer.service';
import {
  CustomerDetail,
  Relationship
} from 'src/business/states/customer-detail.model';
import { CustomerDetailStore } from 'src/business/states/customer-detail.store';

import { BasePage, StringValidator, ValidationError } from '@acts/front-core';
import {
  Component,
  ElementRef,
  Injector,
  OnDestroy,
  OnInit,
  ViewChild
} from '@angular/core';
import {
  AbstractControl,
  FormArray,
  FormBuilder,
  FormControl,
  FormGroup,
  ValidatorFn,
  Validators
} from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';

/**
 * 顧客詳細画面のComponent
 */
@Component({
  selector: 'app-customer-detail',
  templateUrl: './customer-detail.component.html',
  styleUrls: ['./customer-detail.component.scss']
})
export class CustomerDetailComponent extends BasePage
  implements OnInit, OnDestroy {
  @ViewChild('detailCard', { static: false, read: ElementRef })
  public customerCard: ElementRef;

  public _this: any = this;
  /** 顧客詳細情報(入力内容) */
  public detailForm: FormGroup;

  // 続柄配列
  public relationships: Array<string>;

  // 家族情報ポップアップ
  public isVisible: boolean;

  // Default時間
  public birthdayDate = new Date(2000, 1, 1);

  public storeLoading$ = this.customerDetailQuery.selectLoading();
  public showLoading = false;
  public readonly dateFormat = 'yyyy年M月d日';

  public routeParam = {
    refresh: null,
    customerNo: null,
    isEditMode: false
  };

  constructor(
    private customerDetailQuery: CustomerDetailQuery,
    private customerDetailService: CustomerDetailService,
    private customerDetailStore: CustomerDetailStore,
    private customerSearchService: CustomerService,
    private message: NzMessageService,
    private router: Router,
    private activatedRoute: ActivatedRoute,
    private fb: FormBuilder,
    private translate: TranslateService,
    injector: Injector
  ) {
    super(injector);
  }

  /**
   * Component初期処理
   *
   * @memberof CustomerDetailComponent
   */
  public ngOnInit(): void {
    this.initForm();
    this.setErrorSubscribe();
    this.relationships = ['配偶者', '子供', '孫', 'その他'];
    this.initData();
  }

  /**
   * Component破棄時の処理
   *
   * @memberof CustomerDetailComponent
   */
  public ngOnDestroy(): void {}


  /**
   *  顧客情報の保存内容を送信する(新規登録・更新する)
   *
   * @memberof CustomerDetailComponent
   */
  public submitForm(): void {
    const controls = this.detailForm.controls;
    for (const key in controls) {
      if (controls.hasOwnProperty(key)) {
        controls[key].markAsTouched();
        controls[key].updateValueAndValidity();
      }
    }

    setTimeout(() => {
      // form validation check
      if (this.detailForm.valid) {
        const value = this.getFinalData();

        // send request
        if (this.routeParam.customerNo) {
          this.update(value);
        } else {
          this.create(value);
        }
      } else {
        this.message.create(
          'error',
          this.translate.instant('SystemMessages.ValidationErrorTip')
        );
      }
    }, 0);
  }

  /**
   *  施設予約画面へ遷移する
   *
   * @param {MouseEvent} event
   */
  public navigateToFacilityPage(): void {
    const customerNo = this.customerDetailQuery.getValue().customerNo;
    if (customerNo) {
      // navigate
      this.router.navigate(['/acts-demo/facility/book']);
    }
  }

  
   * 初期化家族テーブル
   *
   * @memberof CustomerDetailComponent
   */
  public initialFamilyTable(length: number): void {
    if (length === 0) {
      this.addFamilyInfo();
    }
  }

  public saveFamilyInfo(): void {
    this.isVisible = false;
  }

  public cancelFamilyInfo(): void {
    this.isVisible = false;
  }

  private initForm(): void {
    this.detailForm = this.fb.group(
      {
        global: [null],
        customerNo: [
          null,
          [Validators.required, StringValidator.equalLength(13)]
        ],
        nameKanji: [null, [Validators.required, Validators.maxLength(60)]],
        nameKana: [null, [Validators.required, Validators.maxLength(60)]],
        gender: ['1', [Validators.required]],
        birthday: [null, [Validators.required]],
        addressZip: [null, []],
        telNo: [null, [Validators.required, StringValidator.equalLength(13)]],
        address: [null],
        email: [null, [Validators.email, Validators.maxLength(100)]],
        point: [null, [Validators.required]],
        family: this.fb.array([])
      },
      { updateOn: 'blur' }
    );
  }

  /**
   * Family Control Groupを追加
   *
   * @public
   * @memberof CustomerDetailComponent
   */
  // tslint:disable-next-line:typedef
  public addFamilyGroup() {
    return this.fb.group({
      name: [null, []],
      relationship: [null, [this.checkValidator]]
    });
  }

  get familyArray(): FormArray {
    return <FormArray>this.detailForm.get('family');
  }

  /**
   * 家族情報を追加
   *
   * @public
   * @memberof CustomerDetailComponent
   */
  public addFamilyInfo(): void {
    this.familyArray.push(this.addFamilyGroup());
  }

  /**
   * 家族情報を削除
   *
   * @public
   * @memberof CustomerDetailComponent
   */
  public deleteFamilyInfo(index: number): void {
    this.familyArray.removeAt(index);
  }

  /**
   *
   * load FamilyArray
   * @public
   * @memberof CustomerDetailComponent
   */
  public loadFamilyForm(data: CustomerDetail): void {
    for (let i = 0; i < data.family.length - 1; i++) {
      this.familyArray.push(this.addFamilyGroup());
    }
  }

  /**
   *
   * バリデーション追加
   * @public
   * @memberof CustomerDetailComponent
   */
  public checkValidator = (c: AbstractControl) => {
    const getValue = this.detailForm.value.family;
    if (Object.keys(getValue).length >= 2) {
      console.log('valid', this.familyArray);
      return { errors: '配偶者を二人以上登録することができません' };
    }
    return null;
  };

  private initData(): void {
    this.activatedRoute.queryParams.subscribe((res: any) => {
      if (res.customerNo) {
        this.routeParam.customerNo = res.customerNo;
        this.customerDetailService.getCustomerDetailByID(res.customerNo);
      }
      if (res.refresh) {
        this.routeParam.refresh = res.refresh;
      }
    });

    this.customerDetailQuery
      .select()
      .pipe(
        untilDestroyed(this),
        filter(item => item.customerNo !== '')
      )
      .subscribe((detail: any) => {
        console.log('detailForm', this.detailForm);
        detail = {
          customerNo: '1231234123412',
          nameKanji: 'test0',
          nameKana: 'かな',
          gender: '0',
          birthday: '2019-02-02',
          telNo: '79-0999-1111',
          addressZip: '120-0813',
          address: 'takenotsuka0',
          email: 'slack@test.com',
          point: '1231230',
          family: [
            {
              name: 'テスト1',
              relationship: Relationship.wife
            },
            {
              name: 'テスト2',
              relationship: Relationship.child
            }
          ]
        };
        this.showLoading = false;
        if (detail.error !== null && detail.error) {
          console.log('detail error return');
          return;
        }
        if (this.routeParam.refresh) {
          this.customerSearchService.updateCustomer(
            this.routeParam.customerNo,
            detail
          );
        }
        if (this.routeParam.customerNo !== null) {
          this.detailForm.markAsUntouched();
          this.detailForm.markAsPristine();
          console.log('detail', detail);
          this.loadFamilyForm(detail);
          this.detailForm.patchValue(detail);
          this.detailForm.patchValue({ telNo: this.addHyphen(detail.telNo) });
          this.routeParam.isEditMode = true;
          this.initialFamilyTable(this.detailForm.value.family.length);
        } else {
          this.customerDetailStore.reset();
          this.initialFamilyTable(this.detailForm.value.family.length);
        }
      });
  }

  private setErrorSubscribe(): void {
    this.customerDetailQuery
      .selectError()
      .pipe(
        untilDestroyed(this),
        filter(error => error !== undefined && error !== null)
      )
      .subscribe((error: ValidationError) => {
        this.setFormError(this.detailForm, error, false);
        this.message.create(
          'error',
          this.translate.instant('SystemMessages.ValidationErrorTip')
        );
        this.showLoading = false;
      });

    this.detailForm.valueChanges.pipe(untilDestroyed(this)).subscribe(() => {
      this.detailForm.controls['global'].setErrors(null);
    });
  }


  private navBackAndRefresh(): void {
    this.router.navigate(['/acts-demo/customer/search'], {
      queryParams: {
        refresh: new Date().getTime()
      }
    });
  }
  private create(value: CustomerDetail): void {
    this.showLoading = true;
    this.customerDetailService.create(value).subscribe(() => {
      this.showLoading = false;
      this.navBackAndRefresh();
    });
  }

  private update(value: any): void {
    this.showLoading = true;
    this.customerDetailService.update(value).subscribe((result: boolean) => {
      if (result) {
        this.navigateBack();
      }
      this.showLoading = false;
    });
  }

  private getFinalData(): CustomerDetail {
    const value = this.detailForm.value as CustomerDetail;
    value.telNo = this.removeHyphen(value.telNo);
    value.birthday = this.formatDate(value.birthday);
    return value;
  }

  /**
   * Format date
   *
   * @private
   */
  private formatDate(date: any): any {
    let temp = date;
    if (typeof date === 'object') {
      temp = format(date, 'yyyy-MM-dd');
    }
    return temp;
  }

  /**
   * delete Hyphen
   *
   * @private
   * @param {string} code
   * @returns {string}
   * @memberof CustomerDetailComponent
   */
  private removeHyphen(code: string): string {
    if (!code || code.length === 0) {
      return '';
    }
    return code.replace(/-/g, '');
  }

  /**
   * Add hyphen
   *
   * @private
   * @param {string} targetStr
   * @returns {string}
   * @memberof CustomerDetailComponent
   */
  private addHyphen(targetStr: string): string {
    let changedStr = targetStr;

    if (changedStr.length === 0) {
      changedStr = '';
    } else if (changedStr.length <= 3) {
      changedStr = changedStr.replace(/^(\d{0,3})/, '$1');
    } else if (changedStr.length <= 7) {
      changedStr = changedStr.replace(/^(\d{0,3})(\d{0,4})/, '$1-$2');
    } else {
      changedStr = changedStr.replace(/^(\d{0,3})(\d{0,4})(.*)/, '$1-$2-$3');
    }

    return changedStr;
  }
}
<nz-card [nzBordered]="false" #detailCard class="form-container">
  <nz-spin [nzSpinning]="(storeLoading$ | async) || showLoading">
    <h3>{{ 'CustomerDetail.header.title' | translate }}</h3>
    <div padding>
      <global-error-message
        [form]="detailForm"
        lines="none"
      ></global-error-message>
    </div>
    <form nz-form [formGroup]="detailForm">
      <!-- title -->
      <h4>{{ 'CustomerDetail.form.title' | translate }}</h4>
      <p>{{ 'CustomerDetail.form.info' | translate }}</p>

      <!-- customerNo input -->
      <nz-form-item class="border">
        <div class="form-title required">
          {{ 'CustomerDetail.form.customerNo.title' | translate }}
        </div>
        <div class="form-subtitle"></div>
        <div class="form-content">
          <div class="form-content-item">
            <nz-form-label nzFor="customerNo"
              >{{ 'CustomerDetail.form.customerNo.subTitle' | translate }}
            </nz-form-label>
            <nz-form-control>
              <input
                nz-input
                formControlName="customerNo"
                id="customerNo"
                [disabled]="routeParam.isEditMode"
                [readonly]="routeParam.isEditMode"
                maxlength="13"
                placeholder="{{
                  'CustomerDetail.form.customerNo.placeholder' | translate
                }}"
              />
            </nz-form-control>
          </div>
        </div>
      </nz-form-item>
      <div>
        <validation-messages
          minHeight
          [control]="detailForm.controls['customerNo']"
          [fieldName]="'CustomerDetail.form.customerNo.title'"
        >
        </validation-messages>
      </div>

      <!-- name input -->
      <nz-form-item class="border">
        <div class="form-title required">
          {{ 'CustomerDetail.form.name.title' | translate }}
        </div>
        <div class="form-subtitle">
          <div class="form-subtitle-item">
            <nz-form-label [nzNoColon]="true" nzFor="nameKanji">{{
              'CustomerDetail.form.name.nameKanjiTitle' | translate
            }}</nz-form-label>
          </div>
          <div class="form-subtitle-item">
            <nz-form-label [nzNoColon]="true" nzFor="nameKana">{{
              'CustomerDetail.form.name.nameKanaTitle' | translate
            }}</nz-form-label>
          </div>
        </div>
        <div class="form-content">
          <div class="form-content-item">
            <nz-form-label nzFor="nameKanji">{{
              'CustomerDetail.form.fullWidth' | translate
            }}</nz-form-label>
            <nz-form-control>
              <input
                nz-input
                formControlName="nameKanji"
                id="nameKanji"
                placeholder="{{
                  'CustomerDetail.form.name.nameKanjiPlaceholder' | translate
                }}"
              />
            </nz-form-control>
          </div>
          <div class="form-content-item">
            <nz-form-label nzFor="nameKana">{{
              'CustomerDetail.form.fullWidthKana' | translate
            }}</nz-form-label>
            <nz-form-control>
              <input
                nz-input
                formControlName="nameKana"
                id="nameKana"
                placeholder="{{
                  'CustomerDetail.form.name.nameKanaPlaceholder' | translate
                }}"
              />
            </nz-form-control>
          </div>
        </div>
      </nz-form-item>
      <div>
        <validation-messages
          minHeight
          [control]="detailForm.controls['nameKanji']"
          [fieldName]="'CustomerDetail.form.name.nameKanjiTitle'"
        >
        </validation-messages>
        <validation-messages
          minHeight
          [control]="detailForm.controls['nameKana']"
          [fieldName]="'CustomerDetail.form.name.nameKanaTitle'"
        >
        </validation-messages>
      </div>

      <!-- gender -->
      <nz-form-item class="border">
        <div class="form-title required">
          {{ 'CustomerDetail.form.gender.title' | translate }}
        </div>
        <div class="form-subtitle"></div>
        <div class="form-content">
          <div class="form-content-item">
            <nz-form-label nzFor="gender">{{
              'CustomerDetail.form.gender.subTitle' | translate
            }}</nz-form-label>
            <nz-form-control>
              <nz-radio-group formControlName="gender" id="gender">
                <label nz-radio nzValue="1">{{
                  'Common.gender.male' | translate
                }}</label>
                <label nz-radio nzValue="2">{{
                  'Common.gender.female' | translate
                }}</label>
              </nz-radio-group>
            </nz-form-control>
          </div>
        </div>
      </nz-form-item>

      <!-- date -->
      <nz-form-item class="border">
        <div class="form-title required">
          {{ 'CustomerDetail.form.birthday.title' | translate }}
        </div>
        <div class="form-subtitle"></div>
        <div class="form-content">
          <div class="form-content-item">
            <nz-form-label nzFor="birthday">{{
              'CustomerDetail.form.birthday.subTitle' | translate
            }}</nz-form-label>
            <nz-form-control [nzSpan]="7">
              <nz-date-picker
                formControlName="birthday"
                id="birthday"
                [nzFormat]="dateFormat"
                (ngModelChange)="onBirthdayChange($event)"
                nzPlaceHolder="{{
                  'CustomerDetail.form.birthday.placeholder' | translate
                }}"
                [nzDisabledDate]="disabledDate"
              >
              </nz-date-picker>
            </nz-form-control>
            <nz-form-control [nzSpan]="7">
              <label>({{ birthdayDate | era2wareki }})</label>
            </nz-form-control>
          </div>
        </div>
      </nz-form-item>
      <div>
        <validation-messages
          minHeight
          [control]="detailForm.controls['birthday']"
          [fieldName]="'CustomerDetail.form.birthday.title'"
        >
        </validation-messages>
      </div>

      <!-- tel -->
      <nz-form-item class="border">
        <div class="form-title required">
          {{ 'CustomerDetail.form.telNo.title' | translate }}
        </div>
        <div class="form-subtitle"></div>
        <div class="form-content">
          <div class="form-content-item">
            <nz-form-label nzFor="telNo">{{
              'CustomerDetail.form.telNo.subTitle' | translate
            }}</nz-form-label>
            <nz-form-control>
              <input
                nz-input
                type="tel"
                formControlName="telNo"
                id="telNo"
                maxlength="13"
                appTelNoMask
                [updateMode]="'blur'"
              />
            </nz-form-control>
          </div>
        </div>
      </nz-form-item>
      <div>
        <validation-messages
          minHeight
          [control]="detailForm.controls['telNo']"
          [fieldName]="'CustomerDetail.form.telNo.title'"
        >
        </validation-messages>
      </div>

      <!-- address -->
      <nz-form-item class="border">
        <div class="form-title">
          {{ 'CustomerDetail.form.address.title' | translate }}
        </div>
        <div class="form-subtitle">
          <div class="form-subtitle-item">
            <nz-form-label [nzNoColon]="true" nzFor="nameKanji">{{
              'CustomerDetail.form.address.zipTitle' | translate
            }}</nz-form-label>
          </div>
          <div class="form-subtitle-item">
            <nz-form-label [nzNoColon]="true" nzFor="nameKana">{{
              'CustomerDetail.form.address.addressTitle' | translate
            }}</nz-form-label>
          </div>
        </div>
        <div class="form-content">
          <div class="form-content-item">
            <nz-form-label nzFor="addressZip">{{
              'CustomerDetail.form.halfWidthAlphanumeric' | translate
            }}</nz-form-label>
            <nz-form-control>
              <input
                nz-input
                formControlName="addressZip"
                type="tel"
                maxlength="7"
              />
            </nz-form-control>
          </div>
          <div class="form-content-item">
            <nz-form-label nzFor="address">{{
              'CustomerDetail.form.fullWidth' | translate
            }}</nz-form-label>
            <nz-form-control>
              <input
                nz-input
                formControlName="address"
                id="address"
                placeholder="{{
                  'CustomerDetail.form.address.addressPlaceholder' | translate
                }}"
              />
            </nz-form-control>
          </div>
        </div>
      </nz-form-item>
      <div>
        <validation-messages
          minHeight
          [control]="detailForm.controls['addressZip']"
          [fieldName]="'CustomerDetail.form.address.zipTitle'"
        >
        </validation-messages>
      </div>

      <!-- email -->
      <nz-form-item class="border">
        <div class="form-title">
          {{ 'CustomerDetail.form.email.title' | translate }}
        </div>
        <div class="form-subtitle"></div>
        <div class="form-content">
          <div class="form-content-item">
            <nz-form-label nzFor="email">{{
              'CustomerDetail.form.email.subTitle' | translate
            }}</nz-form-label>
            <nz-form-control>
              <input
                nz-input
                formControlName="email"
                id="email"
                placeholder="{{
                  'CustomerDetail.form.email.placeholder' | translate
                }}"
              />
            </nz-form-control>
          </div>
        </div>
      </nz-form-item>
      <div>
        <validation-messages minHeight [control]="detailForm.controls['email']">
        </validation-messages>
      </div>

      <!-- point -->
      <nz-form-item class="border">
        <div class="form-title  required">
          {{ 'CustomerDetail.form.point.title' | translate }}
        </div>
        <div class="form-subtitle"></div>
        <div class="form-content">
          <div class="form-content-item">
            <nz-form-label nzFor="point" class="label-multiple-line">
              {{ 'CustomerDetail.form.halfWidthNumeral' | translate }}
            </nz-form-label>
            <nz-form-control>
              <input
                nz-input
                formControlName="point"
                id="point"
                maxlength="9"
                placeholder="{{
                  'CustomerDetail.form.point.placeholder' | translate
                }}"
              />
            </nz-form-control>
          </div>
        </div>
      </nz-form-item>

      <!--family-->
      <nz-form-item class="border">
        <div class="form-title">
          {{ 'CustomerDetail.form.family.title' | translate }}
        </div>
        <div class="form-subtitle"></div>
        <div class="form-content" formArrayName="family">
          <div
            *ngFor="let group of familyArray.controls; let i = index"
            [formGroupName]="i"
          >
            <nz-form-item>
              <label class="family-table-label"
                >{{ 'CustomerDetail.form.family.name' | translate }} {{ i + 1 }}
              </label>
              <input
                class="ant-input family-table-input"
                formControlName="name"
              />
              <small id="fnHelp" *ngIf="familyArray.invalid && familyArray.touched" class="form-text custom-invalid">error</small>
              <label class="family-table-label"
                >{{ 'CustomerDetail.form.family.relationship' | translate }}
                {{ i + 1 }}
              </label>
              <select
                class="ant-input family-table-input"
                formControlName="relationship"
              >
                <option *ngFor="let item of relationships" [value]="item">
                  {{ item }}
                </option>
              </select>
              <i
                nz-icon
                nzType="minus-circle-o"
                class="dynamic-delete-button"
                (click)="deleteFamilyInfo(i)"
              ></i>
            </nz-form-item>
          </div>
          <div>
            <button nz-button class="add-button" (click)="addFamilyInfo()">
              <i nz-icon nzType="plus"></i>
              家族追加
            </button>
          </div>
        </div>
      </nz-form-item>

      <!-- actions  [disabled]="detailForm.invalid"-->
      <nz-form-item>
        <nz-form-control [nzSpan]="24" class="actions">
          <button nz-button nzType="primary" (click)="navigateBack()">
            {{ 'CustomerDetail.actions.cancel' | translate }}
          </button>
          <button nz-button nzType="primary" (click)="submitForm()">
            {{ 'CustomerDetail.actions.save' | translate }}
          </button>
          <button
            *ngIf="routeParam.customerNo"
            nz-button
            nzType="primary"
            (click)="navigateToFacilityPage()"
          >
            {{ 'CustomerDetail.actions.next' | translate }}
          </button>
        </nz-form-control>
      </nz-form-item>
    </form>
  </nz-spin>
</nz-card>

相关问题