首页 > 解决方案 > 如何从用户选择的选项绑定到 Angular 表单

问题描述

好吧,比标题复杂一点。。

我正在处理的这个表单是一个表单组。它有几个字段(补充名称、描述和标签),补充名称之一是我需要帮助的,因为我没有处理过像这样的复杂表格,我想把它做好,而不仅仅是提供一个凌乱的补丁工作。

这是发生的预期逻辑顺序

  1. 例如,用户通过单击该字段添加新的补充并开始输入“肌酸”
  2. 发出一个查询,根据输入的条目获取产品并返回作为建议提供的 JSON
  3. 用户点击建议“肌酸”
  4. 字段已填充并绑定
  5. 我们通过“添加选项”添加另一个条目,并重复我们要添加的 X 数量的产品。

实际发生了什么

  1. 用户通过单击字段添加新补充并键入“肌酸”建议请求被发送并填充下拉列表
  2. 用户点击建议“肌酸”该字段采用该值
  3. 值实际上是空白
  4. 用户添加了另一个补充,但之前的选择在该字段中
  5. 用户清除它并再次键入
  6. 值为空

需要发生的是用户可以添加 X 数量的补充,并能够从下拉推荐中获取任何选项,并将其添加到表单组数组中,并且不会干扰其他表单组数组值。

我知道这不是绑定表单的正确方法,而且我认为绑定 mat 输入字段以触发查询的方式也不正确,这就是我再次提出问题的原因,而不是提供补丁工作。

组件代码

import { Subscription } from 'rxjs/Subscription';
import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { UtilitiesService } from '../../utilities/utilities.service';
import { GetSupplementsService } from './get-supplements.service';
@Component({
  selector: 'app-supplements',
  templateUrl: './supplements.component.html',
  styleUrls: ['./supplements.component.css'],
  providers: [GetSupplementsService],
})

export class SupplementsComponent implements OnInit {
  supplementForm: FormGroup;
  queryField: FormControl = new FormControl();
  private supplementInventoryResults: Array<ISupplementInventoryResponse>;
  private eventForm: FormGroup;
  private searchResults: any;

  private searchSubscription: Subscription;
  private addSupplementSubscription: Subscription;

  subcription: Subscription;

  constructor (
    private bottomSheet: MatBottomSheet,
    private _fb: FormBuilder,
    private ref: ChangeDetectorRef,
    private _utils: UtilitiesService,
    private getSupplements: GetSupplementsService,
    private router: Router
  ) { }

  public ngOnInit(): void {
    this.browsingStackHistory = false;
    this.loading = true;

    
    this.supplementForm = this._fb.group({ // the form in question
      entryArray: this._fb.array([
        this.getUnit()
      ])
    });

    this.searchSubscription =
      this.queryField.valueChanges
        .debounceTime(600)
        .distinctUntilChanged()
        .switchMap((query) => this.getSupplements.search_supplement_by_category(query))
        .subscribe((result) => {
          this.searchResults = result;
        });
  }

  public ngOnDestroy(): void {
    this.subcription.unsubscribe();
  }


  private getUnit(): FormGroup {
    return this._fb.group({
      supplementName: [''],
      description: [''],
      tags: ['']
    });
  }

  private addUnit(): void {
    const control = <FormArray>this.supplementForm.controls['entryArray'];
    control.push(this.getUnit());
  }

  private removeUnit(i: number): void {
    const control = <FormArray>this.supplementForm.controls['entryArray'];
    control.removeAt(i);
  }

  private addSupplement(): void { // this will do the post to the service
    const supplementObject = {
      start: this._utils.get_upload_time(),
      data: this.supplementForm.value,
      rating: 0
    };

  }
}

模板

[![<mat-tab label="Add Stack (Test)">
    <div style="padding:8px;">
      <div fxLayout="row wrap">
        <div fxFlex.gt-sm="50%" fxFlex="100">
          <h1>Add New Supplements Stack</h1>
          <form \[formGroup\]="supplementForm" class="log-workout">
            <!-- Start form units array with first row must and dynamically add more -->
            <div fxLayout="column" fxLayoutAlign="center center" class="row-height">
              <div formArrayName="entryArray">
                <mat-divider></mat-divider>
                <!-- loop throught units -->
                <div *ngFor="let reps of supplementForm.controls.entryArray.controls; let i=index">
                  <!-- row divider show for every nex row exclude if first row -->
                  <mat-divider *ngIf="supplementForm.controls.entryArray.controls.length > 1 && i > 0"></mat-divider>
                  <br>
                  <!-- group name in this case row index -->
                  <div \[formGroupName\]="i">
                    <!-- unit name input field -->
                    <div class="row">
                      <mat-form-field class="example-form">
                        <input matInput placeholder="Supplement Name" \[formControl\]="addSupplementField"
                          formControlName="supplementName" \[matAutocomplete\]="auto">
                        <mat-autocomplete #auto="matAutocomplete">
                          <mat-option *ngFor="let product of supplementResults" \[value\]="product?.product_name">
                            <img class="example-option-img" aria-hidden \[src\]="product?.product_image" height="25">
                            {{product?.product_name}}
                          </mat-option>
                        </mat-autocomplete>
                      </mat-form-field>
                      <mat-form-field class="example-form">
                        <input matInput placeholder="Description" formControlName="description" required>
                      </mat-form-field>
                      <mat-form-field class="example-form">
                        <input matInput placeholder="Tags" formControlName="tags" required>
                      </mat-form-field>
                    </div>
                    <!-- row delete button, hidden if there is just one row -->
                    <button mat-mini-fab color="warn" *ngIf="supplementForm.controls.entryArray.controls.length > 1"
                      (click)="removeUnit(i)">
                      <mat-icon>delete forever</mat-icon>
                    </button>
                  </div>
                </div>
                <!-- New unit button -->
                <mat-divider></mat-divider>
                <mat-card-actions>
                  <button mat-raised-button (click)="addUnit()">
                    <mat-icon>add box</mat-icon>
                    Add Other Product
                  </button>
                </mat-card-actions>
                <button mat-raised-button (click)="addSupplement()">
                  <mat-icon>add box</mat-icon>
                  Add Supplement
                </button>
              </div>
            </div>
            <!-- End form units array -->
          </form>
        </div>
      </div>
    </div>][1]][1]

标签: javascriptangularforms

解决方案


在调用 getUnit() 函数时具有以下内容显然会绑定它,因为它将独立运行且不会发生冲突。

  private getUnit(): FormGroup {
    const formGroup = this._fb.group({
      supplementName: [''],
      review: [''],
      rating: [''],
      notes: [''],
      tags: ['']
    });

    formGroup.get('supplementName').valueChanges
      .debounceTime(300)
      .distinctUntilChanged()
      .switchMap((search) => this.getSupplements.search_supplement_by_category(search))
      .subscribe((products) => {
        this.supplementResults = products;
      });
    return formGroup;
  }

推荐阅读