在Angular 5中,FormsArray中的formArray.controls是什么意思?

4

我是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是什么意思?根据Angular的说法,它的意思是:

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

secretlairs.controls是否包含任何数据?我能用任意类型的数据替换这一部分,并用从WebAPI获取的数据进行实例化吗?例如,

*ngFor="let address of secretLairs.controls

我使用

*ngFor="let address of addresses

其中addresses是任意类型,数据来自数据库。

1个回答

7

首先,有三种类型的表单 - FormControlFormGroupFormArray - 都继承自AbstractControl

当您使用响应式表单进行验证并在组件模板中包括formGroupNameformControlNameformArrayName时,实际上您在声明性地定义表单控件树与根FormGroup模型之间的映射关系。

例如,给定以下模板:

<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>

您正在声明性地设置一个表单映射来收集信息,最终将产生一个特定格式的JSON对象。例如,给定上述表单模型,formGroup.value将返回:

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

在你的模板中声明表单组的结构后,你需要在组件类中通过编程方式创建相应的 formGroupformControlformArray。在设置每个表单时,你可以设置其他参数:

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

这适用于任何抽象控件。

根据上述模板,对应的formGroup模型将如下所示:

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)))
    })
  }
}

要绑定到表单的任何验证标志,您可以使用根formGroup和路径字符串(支持点)来查找特定的组、控件或数组。

例如:

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

希望这可以清楚明白。
[编辑]
更好的做法是,由于反应式表单完全是在模板中声明性地设置模型,并在组件类中以命令方式映射相同的模型,因此您应该考虑定义一个JSON模型并使用它。
例如,假设我们有一个自定义模型MyModel,其中具有firstName属性、lastName属性和cities属性。组件类应如下所示:
 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);
     }
  }
}

你好,感谢您的输入。这确实让整个概念更加清晰了。但是,您能否解释一下formarray.controls具体指的是什么?formarray.controls中的每个控件是否都指向formgroup?我能否用数据模型代替控件来替换这部分内容? - sanmatrix
我所举的例子是数据模型(而不是控制模型)。 - pixelbits
我明白了,所以只是想指出并不总是需要使用控件模型对吧?这意味着如果我选择使用数据模型,我仍然可以将用户输入绑定到表单字段上吗?(通过您在示例中提到的FormControl名称) - sanmatrix
是的,那是正确的。您可以绑定到常规数据模型或控件模型。 - pixelbits
这个很好地解释了问题。谢谢。我会点赞并将其选为正确答案。 - sanmatrix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接