我是Angular 2的新手,我认为学习Angular 2的最好方法是阅读官方的Angular指南.

我阅读了《react 性表格指南》(Reactive Forms Guide) https://angular.io/guide/reactive-forms

演示链接:https://stackblitz.com/angular/jammvmbrpxle

虽然内容总体上相当不错,但我一直在思考如何实现更复杂的表单.在给定的示例中,每个英雄都可能有许多地址.地址本身是平面对象.

如果地址有其他信息,例如地址所在房间的 colored颜色 和类型,该怎么办.

export class Address {
    street = '';
    city   = '';
    state  = '';
    zip    = '';
    rooms = Room[];
}

export class Room {
     type = '';
}

这样表单模型就会看起来像这样.

createForm() {
this.heroForm = this.fb.group({
  name: '',
  secretLairs: this.fb.array([
      this.fb.group({
          street: '',
          city: '',
          state: '',
          zip: '',
          rooms: this.fb.array([
              this.fb.group({
                 type: ''
          })]),
      })]),
  power: '',
  sidekick: ''
});

}

EDIT - Finalized Code that works with ngOnChanges

英雄细节.组成部分ts

createForm() {
    this.heroForm = this.fb.group({
      name: '',
      secretLairs: this.fb.array([
        this.fb.group({
          street: '',
          city: '',
          state: '',
          zip: '',
          rooms: this.fb.array([
            this.fb.group({
              type: ''
            })
          ])
        })
      ]),
      power: '',
      sidekick: ''
    });
  }

  ngOnChanges() {
    this.heroForm.reset({
      name: this.hero.name,
    });
    this.setAddresses(this.hero.addresses);
  }

  setAddresses(addresses: Address[]) {
    let control = this.fb.array([]);
    addresses.forEach(x => {
      control.push(this.fb.group({
        street: x.street,
        city: x.city,
        state: x.state,
        zip: x.zip,
        rooms: this.setRooms(x) }))
    })
    this.heroForm.setControl('secretLairs', control);
  }

  setRooms(x) {
    let arr = new FormArray([])
    x.rooms.forEach(y => {
      arr.push(this.fb.group({ 
        type: y.type 
      }))
    })
    return arr;
  }

英雄细节.组成部分html(嵌套表单数组部分)

<div formArrayName="secretLairs" class="well well-lg">
  <div *ngFor="let address of heroForm.get('secretLairs').controls; let i=index" [formGroupName]="i" >
    <!-- The repeated address template -->
    <h4>Address #{{i + 1}}</h4>
    <div style="margin-left: 1em;">
      <div class="form-group">
        <label class="center-block">Street:
          <input class="form-control" formControlName="street">
        </label>
      </div>
      <div class="form-group">
        <label class="center-block">City:
          <input class="form-control" formControlName="city">
        </label>
      </div>
      <div class="form-group">
        <label class="center-block">State:
          <select class="form-control" formControlName="state">
            <option *ngFor="let state of states" [value]="state">{{state}}</option>
          </select>
        </label>
      </div>
      <div class="form-group">
        <label class="center-block">Zip Code:
          <input class="form-control" formControlName="zip">
        </label>
      </div>
    </div>
    <br>
    <!-- End of the repeated address template -->
    <div formArrayName="rooms" class="well well-lg">
      <div *ngFor="let room of address.get('rooms').controls; let j=index" [formGroupName]="j" >
          <h4>Room #{{j + 1}}</h4>
          <div class="form-group">
            <label class="center-block">Type:
              <input class="form-control" formControlName="type">
            </label>
          </div>
      </div>
    </div>
  </div>
  <button (click)="addLair()" type="button">Add a Secret Lair</button>
</div>

推荐答案

编辑:2021年,因为排版变得更严格了(好!)我们需要做一些改变.不能使用getter来键入嵌套的formarray.您可以改用函数,但我不喜欢这个 idea ,因为每次检测更改时都会调用该函数.取而代之的是,我绕过了打字判断,转而使用['controls'].如果您确实想要更强的嵌套数组(项目)类型,请使用函数,但请记住,每次检测更改时都会调用该函数……下面是更新后的代码:

具有嵌套的窗体数组没有太大区别.基本上你只需要复制你拥有的代码.使用嵌套数组:),下面是一个示例:

myForm: FormGroup;

constructor(private fb: FormBuilder) {
  this.myForm = this.fb.group({
    // you can also set initial formgroup inside if you like
    companies: this.fb.array([])
  })
}

// getter for easier access
get companiesFormArr(): FormArray {
  return this.myForm.get('companies') as FormArray;
}

addNewCompany() {
  this.companiesFormArr.push(
    this.fb.group({
      company: [''],
      projects: this.fb.array([])
    })
  );
}

deleteCompany(index: number) {
  this.companiesFormArr.removeAt(index);
}

这就是最外层表单数组的添加和删除操作,因此向嵌套表单数组添加和删除表单组只是复制代码.其中,我们从模板中传递当前的formgroup,您要向其中添加(在本例中)新项目/删除项目的array.

addNewProject(control) {
  control.push(
    this.fb.group({
      projectName: ['']
  }))
}

deleteProject(control, index) {
  control.removeAt(index)
}

以同样的方式,你迭代你的外部表单数组,然后在里面迭代你的内部表单数组:

<form [formGroup]="myForm">
  <div formArrayName="companies">
    <div *ngFor="let comp of companiesFormArr.controls; let i=index">
    <h3>COMPANY {{i+1}}: </h3>
    <div [formGroupName]="i">
      <input formControlName="company" />
      <button (click)="deleteCompany(i)">
         Delete Company
      </button>
      <div formArrayName="projects">
        <!-- Here I has worked around the typechecking, 
             if you want stronger typechecking, call a function. 
             Remember: function called on each change detection! -->
        <div *ngFor="let project of comp.get('projects')['controls']; let j=index">
          <h4>PROJECT {{j+1}}</h4>
          <div [formGroupName]="j">
            <input formControlName="projectName" />
            <button (click)="deleteProject(comp.get('projects'), j)">
              Delete Project
            </button>
          </div>
        </div>
        <button (click)="addNewProject(comp.get('projects'))">
          Add new Project
        </button>
      </div>
    </div>
  </div>
</div>

演示

编辑:

要在有数据后为表单设置值,可以调用以下方法来迭代数据并为表单设置值.在本例中,data看起来像:

data = {
  companies: [
    {
      company: "example comany",
      projects: [
        {
          projectName: "example project",
        }
      ]
    }
  ]
}

我们调用setCompanies为我们的表单设置值:

setCompanies() {
  this.data.companies.forEach(x => {
    this.companiesFormArr.push(this.fb.group({ 
      company: x.company, 
      projects: this.setProjects(x) }))
  })
}

setProjects(x) {
  let arr = new FormArray([])
  x.projects.forEach(y => {
    arr.push(this.fb.group({ 
      projectName: y.projectName 
    }))
  })
  return arr;
}

Angular相关问答推荐

cat Owl carousel 位置下一个和上一个图片

Toastr在独立组件上的Angular17实现

命令不起作用的初始菜单项

如何将Angular 服务包含在延迟加载的独立组件路由中?

如果我只在组件中使用某个主题,是否需要取消订阅该主题?

除非将 signal.set 作为间隔的一部分调用,否则Angular 信号效果不会执行

在switchMap操作符中使用条件语句以进行第一次或随后的执行

使用 BehaviorSubject 从服务更新 Angular 组件

RxJS 基于先前的数据响应执行请求(涉及循环对象数组)

有条件地取消所有内部可观察对象或切换到仅发出一个值的新对象

当我ng serve时,Angular CLI 提示TypeError: callbacks[i] is not a function

Angular 2 二传手与 ngOnChanges

带有重定向的Angular 2 AuthGuard服务?

如何在 Angular 4 中获取 HttpClient 状态码

即使 withCredentials 为真,Angular 也不会发送在 Set-Cookie 中收到的 Cookie

Angular @NGRX中这三个点的含义是什么

Angular 中 polyfills.ts 文件有什么用

RxJS:takeUntil() Angular 组件的 ngOnDestroy()

如何从materialAngular使用分页器?

如何使用无头浏览器运行测试?