如何在Angular2中为表单分配和验证数组

Rez*_*eza 6 javascript typescript angular2-formbuilder angular

this.profile在javascript中的model()有一个名为的属性emails,它是一个数组{email, isDefault, status}

然后我将其定义如下

  this.profileForm = this.formBuilder.group({
    .... other properties here
    emails: [this.profile.emails]
  });

  console.log(this.profile.emails); //is an array
  console.log(this.profileForm.emails); // undefined
Run Code Online (Sandbox Code Playgroud)

在html文件中我用它作为

    <div *ngFor="let emailInfo of profileForm.emails">
        {{emailInfo.email}}
        <button (click)="removeEmail(emailInfo)">
           Remove 
        </button>
    </div>
Run Code Online (Sandbox Code Playgroud)

如果我不添加它formGroup并将其用作数组 - 如下所示 - 它工作正常,但我有一个业务规则,该数组不应该为空,我无法根据此长度设置表单验证

  emails : [];
  this.profileForm = this.formBuilder.group({
    .... other properties here
  });

  this.emails = this.profile.emails;
  console.log(this.profile.emails); //is an array
  console.log(this.emails); // is an array
Run Code Online (Sandbox Code Playgroud)

我尝试使用formBuilder.array但是那个是控件数组而不是数据数组.

   emails: this.formBuilder.array([this.profile.emails])
Run Code Online (Sandbox Code Playgroud)

那么我的问题是如何将数组从模型绑定到UI以及如何验证数组的长度?

art*_*ian 6

这对我有用(角度2.1.2),这种方法使您可以灵活地为您的电子邮件定义自定义验证:

 this.profileForm = this.formBuilder.group({
    emails: [this.profile.emails, FormValidatorUtils.nonEmpty]
    // ......
  });

export class FormValidatorUtils {

  static nonEmpty(control: any) {
    if (!control.value || control.value.length === 0) {
      return { 'noElements': true };
    }
    return null;
  }
}
Run Code Online (Sandbox Code Playgroud)


dev*_*033 6

我应该如何将数组从模型绑定到UI?

好吧,我更愿意推动所有电子邮件profile.emailsformArray,否则你将有值,但没有验证.

我该如何验证数组的长度?

您可以使用Validators.minLength(Number)任何其他控件.

演示代码:

零件:

export class AnyComponent implements OnInit {

  profileForm: FormGroup;
  emailsCtrl: FormArray;

  constructor(private formBuilder: FormBuilder) { }

  ngOnInit(): void {

    this.emailsCtrl = this.formBuilder.array([], Validators.minLength(ANY_NUMBER));
    this.profile.emails.forEach((email: any) => this.emailsCtrl.push(this.initEmail(email)));

    this.profileForm = this.formBuilder.group({
      // ... other controls
      emails: this.emailsCtrl
    });
  }

  private initEmail = (obj: any): FormGroup => {
    return this.formBuilder.group({
      'email': [obj.email], //, any validation],
      'isDefault': [obj.isDefault] //, any validation]
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

模板:

<div *ngFor="let emailInfo of emailsCtrl.value">
  {{emailInfo.email}}
  <button (click)="removeEmail(emailInfo)">
    Remove
  </button>
</div>
<div *ngIf="emailsCtrl.hasError('minlength')">
  It should have at least {{emailsCtrl.getError('minlength').requiredLength}} emails
</div>
Run Code Online (Sandbox Code Playgroud)

PS1:注意Validators.minLength(param) 方法的参数必须大于1,否则它将无法验证.

正如您在源代码中看到的那样,当控件为空时,它会自动返回null.

然后,为了使它按预期工作,您可以添加required Validator:

this.emailsCtrl = this.formBuilder.array([], Validators.compose([Validators.required, Validators.minLength(ANY_NUMBER > 1)]);
Run Code Online (Sandbox Code Playgroud)

在模板中:

<div *ngIf="emailsCtrl.invalid">
  <span *ngIf="emailsCtrl.hasError('required')">
    It's required
  </span>
  <span *ngIf="emailsCtrl.hasError('minlength')">
    It should have at least {{emailsCtrl.getError('minlength').requiredLength}} emails
  </span>
</div>
Run Code Online (Sandbox Code Playgroud)

PS2:

我认为在您的removeEmail 函数中传递要删除的电子邮件的索引更有意义,因此您不必调用indexOf以获取特定 的索引email.你可以这样做:

<div *ngFor="let emailInfo of emailsCtrl.value; let i = index">
  {{emailInfo.email}}
  <button (click)="removeEmail(i)">
    Remove
  </button>
</div>
Run Code Online (Sandbox Code Playgroud)

零件:

removeEmail(i: number): void {
  this.emailsCtrl.removeAt(i);
}
Run Code Online (Sandbox Code Playgroud)

看看这个简单 DEMO