Angular: 在异步调用和 AsyncPipe 之后创建动态响应式表单

Angular: create dynamic Reactive Form after async call & AsyncPipe

我执行异步服务调用以获取元素列表。

 public elementList: Array<Element>;
   ... 
   this.service.getElementList().subscribe( list => {
       this.elementList = list;
       this.createFormGroup(list);
     }
   );
   ...

当我收到元素列表时(我订阅了服务调用),我会根据元素列表构建表单组。 在模板中,我有一个 *ngIf 仅当列表大于零时才绘制。

*ngIf="elementList?.length>0"

由于elementList是异步获取的,所以这个ngIf不起作用。我读过我可以将 ngIf 与 AsnycPipe 一起使用。所以在模板中我将有:

*ngIf="elementList$ && elementList$.length>0"

其中 elementList$ 是从服务调用返回的 Observable。

   public elementList$: Observable<Array<Element>>;   

   elementList$ =this.service.getElementList()

我想知道如何在收到 elementList 后创建 FormGroup。首先,我需要创建 FormGroup,然后模板应调用 *ngIf 来绘制元素。

首先(但只是吹毛求疵),我认为 "painting" 元素的正确术语应该是 "rendering" 元素。(老实说,我对你使用的术语没问题。)

关于问题本身,您可以通过管道 Observable 列表通过 async 管道检查列表的长度,然后检查该管道列表的长度:

*ngIf="elementList$ && (elementList$ | async)?.length > 0"

可以有许多不同的方法来实现这一点。我现在只分享两种方法,如果您觉得这些方法中的任何一种不能满足您的用例,我们可以考虑采用不同的方法(前提是您提供更多信息,说明您究竟想在此处实现什么) .

方法一:

您的组件有两个属性:

form$: Observable<FormGroup>;
list: Array<Element>;

一旦你有了 API 响应,而不是 subscribe 它,你 map 它并生成一个表单,同时还将响应的值分配给 list 属性 您已声明。像这样:

this.form$ = this.service.getElements()
  .pipe(
    map((list: Array<Element>) => {
      this.list = list;
      return this.fb.group({
        elementId: [list[0].id],
        elementDescription: [list[0].description]
      });
    })
  );

然后在模板中使用它,有点像这样:

<form 
  *ngIf="form$ | async as form"
  [formGroup]="form">
  <label for="">Element Id: </label>
  <select formControlName="elementId">
    <option 
      *ngFor="let element of list"
      [value]="element.id">
      {{ element.id }}
    </option>
  </select>
  <br>
  <label for="">Element description: </label>
  <select formControlName="elementDescription">
    <option 
      *ngFor="let element of list"
      [value]="element.description">
      {{ element.description }}
    </option>
  </select>
</form>

方法二:

您可能希望将列表和 FormGroup 组合在一起。所以你可以在你的组件中创建一个 属性:

elementListWithForm$: Observable<{ list: Array<Element>, form: FormGroup }>;

然后您将像这样分配一个值:

this.elementListWithForm$ = this.service.getElements()
  .pipe(
    map((list: Array<Element>) => ({ 
      form: this.fb.group({
        elementId: [list[0].id],
        elementDescription: [list[0].description]
      }),
      list,
     }))
  );

然后你可以像这样在模板中使用它:

<form 
  *ngIf="(elementListWithForm$ | async) as formWithList"
  [formGroup]="formWithList.form">
  <label for="">Element Id: </label>
  <select formControlName="elementId">
    <option 
      *ngFor="let element of formWithList.list"
      [value]="element.id">
      {{ element.id }}
    </option>
  </select>
  <br>
  <label for="">Element description: </label>
  <select formControlName="elementDescription">
    <option 
      *ngFor="let element of formWithList.list"
      [value]="element.description">
      {{ element.description }}
    </option>
  </select>
</form>

Here's a Working Code Sample on StackBlitz for your ref.

PS: This approach is heavily inspired by the one I used in an article that I wrote about increasing the performance of a deeply nested reactive form in Angular for AngularInDepth. You might want to check that out as well.

希望这对您有所帮助。 :)

根据@SiddAjmera的回答,我构建了满足我需求的第三个选项。

方法三:

formGroup: FormGroup;
elementList$: Observable<Array<Element>>;

收到 API 响应后:

    this.elementList$ = this.service.getElements()
    .pipe(
        map((list: Array<Element>) => {
            this.formGroup = this.fb.group({
                elementId:          [list[0].id],
                elementDescription: [list[0].description]
            });
            return list;
        })
    );

然后在模板中我有这个:

    <form
      *ngIf="(elementList$ | async) as list"
            [formGroup]="formGroup"  (ngSubmit)="onSubmit()">
      <label for=''>Element Id: </label>
      <select formControlName="elementId">
        <option
                *ngFor="let element of list"
                [value]="element.id">
          {{ element.id }}
        </option>
      </select>
      <br>
      <label for=''>Element description: </label>
      <select formControlName="elementDescription">
        <option
                *ngFor="let element of list"
                [value]="element.description">
          {{ element.description }}
        </option>
      </select>  
      <br>
      <button type="submit">Click</button
</form>

在组件中,当我单击按钮时,我访问表单组以获取值:

public onSubmit() {
    console.log(this.formGroup.toString());
}