首页 > 解决方案 > formsarray 中的 formarray.controls 在角度 5 中是什么意思?

问题描述

我是 Angular 5 的新手,所以基本上我仍然掌握这些概念。以 Angular 的响应式表单文档为例(https://angular.io/guide/reactive-forms),给出以下代码:

<div formArrayName="secretLairs" class="well well-lg">
<div *ngFor="let address of secretLairs.controls; let i=index" 
 [formGroupName]="i" >
    <!-- The repeated address template -->
  </div>
  </div>

secretlairs.controls 是什么意思,它是什么?根据角度,这意味着:

重复项的来源是 FormArray.controls,而不是 FormArray 本身。每个控件都是一个地址 FormGroup,这正是之前(现在重复)模板 HTML 所期望的。

secretlairs.controls 是否包含任何数据?我可以用任何类型的数据替换这个部分,并用从 webapi 获得的数据实例化一个对象本身吗?例如,而不是

*ngFor="let address of secretLairs.controls

我用

*ngFor="let address of addresses

其中地址是任何类型的,数据是从数据库中获取的。

标签: angular

解决方案


First, there are three type of forms - FormControl, FormGroup, and FormArray - all inherit from AbstractControl.

When you use Reactive Forms for validation and include either formGroupName, formControlName, or formArrayName in the component's template, you are actually declaratively defining a map between the form control tree and the root FormGroup model.

For example, given the following template:

<div [formGroup]="formGroup">
    <div formGroupName="personalInfo">
         First Name: <input type="text" formControlName="firstName"><br />
         Last Name: <input type="text" formControlName="lastName"><br />
    </div>
    <div formArrayName="cities">
        Top cities: <input *ngFor="let city of cities; index as i" type="text" [formControlName]="i">
    </div>
</div>

You are declaratively setting up a form map for collecting information, which will eventually produce a JSON object in a specific format. For example, given the above form model, formGroup.value would return:

{
   "personalInfo": {
        "firstName: 'John',
        "lastName: 'Smith'
    },
    "cities": [
        "New York",
        "Winnipeg",
        "Toronto"
    ]
 }

Once you've declared the structure of your form group in your template, you need to imperatively create the corresponding formGroup, formControl, and formArray in your component class. As you setup each form, you have the opportunity to set up additional parameters:

  1. Initial Form Value
  2. Array of synchronous validators
  3. Array of asynchronous validators

This applies to any of the abstract controls.

This is what the corresponding formGroup model would look like given the above template:

export class AppComponent {
  firstName: string,
  lastName: string;
  cities: string[];
  @Input() formGroup: FormGroup;
  constructor(private fb: FormBuilder) {
    // setup initial values
    this.cities = ['New York', 'Winnipeg', 'Toronto'];
    this.firstName = 'John';
    this.lastName  = 'Smith';

    // create a formGroup that corresponds to the template
    this.formGroup = fb.group({
      firstName: [this.firstName, Validators.required],
      lastName: [this.lastName, Validators.required],
      cities: fb.array(this.cities.map(t=> fb.control(t, Validators.required)))
    })
  }
}

To bind to any of the form's validation flags, you can use the root formGroup and a path string (which supports dots) to find the particular group, control, or array.

For example:

<div *ngIf="formGroup.get('firstName').errors.required">First Name is Required</div>

Hopefully, that makes it clear.

[Edit]

Better yet, given that Reactive Forms is all about declaratively setting up a model in the template, and mapping the same model imperatively in the component class, you should consider defining a JSON model and using that instead.

For example, suppose we have a custom model MyModel which has a firstName property, lastName property, and a cities property. The component class would look like this:

 export class AppComponent {
  @Input() model: MyModel;
  @Output() modelChange: EventEmitter<MyModel>;

  @Input() formGroup: FormGroup;
  constructor(private fb: FormBuilder) {
    this.model = new EventEmitter<MyModel>();
    // create a formGroup that corresponds to the template
    this.formGroup = fb.group({
      firstName: [this.model.firstName, Validators.required],
      lastName: [this.model.lastName, Validators.required],
      cities: fb.array(this.model.cities.map(t=> fb.control(t, Validators.required)))
    });
  }

  onSubmit() {
     if (this.formGroup.valid) {
        this.model = this.formGroup.value;
        this.modelChange.next(this.model);
     }
  }
}

推荐阅读