5

我使用模板表单构建了一个包含大量输入的大表单。现在我需要动态添加一部分输入。由于使用 Reactive Form 动态添加输入似乎更容易,我想将输入的特定部分更改为 Reactive Form。

那么是否可以在同一个表单标签中混合使用响应式表单和模板表单?

4

4 回答 4

12

您可以混合使用响应式表单和模板驱动表单,但强烈不建议这样做。这是因为ngModel在响应式表单上使用违背了表单状态不变性的想法。

响应式表单的原则遵循“单向”数据绑定规则,即您遵循管理表单状态的不可变方法,以便在模板和组件逻辑之间有更大的关注点分离。您可以在第一段的链接上阅读更多关于反应式表单的优点。

假设您正在继续混合模板驱动的表单和反应式表单。运行时控制台会抛出以下错误ng serve

看起来您在与 formControlName 相同的表单字段上使用 ngModel。Angular v6 中已弃用支持使用带有反应式表单指令的 ngModel 输入属性和 ngModelChange 事件,并将在 Angular v7 中删除有关此内容的更多信息,请参阅我们的 API 文档:https ://angular.io/api/forms /FormControlName#use-with-ngmodel

于 2019-04-18T05:28:13.657 回答
2

我在上面发布的链接摘录/https://blog.angular-university.io/introduction-to-angular-2-forms-template-driven-vs-model-driven/

Section :但是 ngModel 发生了什么?

请注意,ngModel 仍然可以与反应形式一起使用。只是表单值在两个不同的地方可用:视图模型和 FormGroup,这可能会导致一些混乱。

于 2019-04-18T05:23:57.300 回答
2

是的,您可以先将两者一起使用创建一个反应式表单,然后根据您的要求添加模板驱动它的工作原理。请参阅角度文档如何将两者一起使用

于 2019-04-18T05:53:29.057 回答
0

是的,你可以,检查这个链接它是完整的反应形式,但它接近你的场景,但你需要做的是进行一些修改以匹配你的情况。就我而言,我所做的是:

1-在我的表单中添加以下标签:

//下面的响应式表单的标签应该在模板驱动的表单内


   <mat-tab [label]="'Invoices' | localize">
        <mat-card-content [formGroup]="exampleForm">
      <!-- Start form units array with first row must and dynamically add more -->
      <mat-card formArrayName="units"  >
        <mat-card-title>Units</mat-card-title>
        <mat-divider></mat-divider>

        <!-- loop throught units -->
        <div *ngFor="let unit of exampleForm.controls.units.controls; let i=index" >

          <!-- row divider show for every nex row exclude if first row -->
          <mat-divider *ngIf="exampleForm.controls.units.controls.length > 1 && i > 0" ></mat-divider><br>

          <!-- group name in this case row index -->
          <div [formGroupName]="i">
            <div fxLayout="row" fxLayout.xs="column" fxLayoutWrap fxLayoutGap="3.5%" fxLayoutAlign="center">

              <!-- unit name input field -->
              <mat-form-field  fxFlex="30%"> 
                <input matInput placeholder="Unit name" formControlName="unitName" required>  
                <!-- input field error -->
                <mat-error *ngIf="unit.controls.unitName.invalid">
                    Unit name is required.        
                </mat-error>            
              </mat-form-field>


              <!-- unit quantity input field -->
              <mat-form-field  fxFlex="10%" fxFlex.xs="20"> 
                <input matInput placeholder="Quantity" type="number" formControlName="qty" required>
              </mat-form-field>

              <!-- unit price input field -->
              <mat-form-field  fxFlex="20%"  fxFlex.xs="grow"> 
                <input matInput placeholder="Unit price" type="number" formControlName="unitPrice" required>
              </mat-form-field>

              <!-- unit total price input field, calculated and not editable -->  
              <div fxLayout.xs="row">
              <mat-form-field  > 
                <input matInput placeholder="Total sum" formControlName="unitTotalPrice">
              </mat-form-field>

              <!-- row delete button, hidden if there is just one row -->
              <button type="button" mat-mini-fab color="warn" fxFlex="nogrow"
                      *ngIf="exampleForm.controls.units.controls.length > 1" (click)="removeUnit(i)">
                  <mat-icon>delete forever</mat-icon>
              </button>
              </div>
            </div>
          </div>
        </div>

        <!-- New unit button -->
        <mat-divider></mat-divider>
        <mat-card-actions>
          <button type="button" mat-raised-button (click)="addUnit()">
            <mat-icon>add box</mat-icon>
            Add new unit
          </button>
          <button type="button" mat-raised-button (click)="clearAllUnits()">
            <mat-icon>remove_circle</mat-icon>
            Clear all
          </button>

        </mat-card-actions>
      </mat-card> <!-- End form units array -->
      <br>
      <!-- Total price calculation formated with angular currency pipe -->
      <mat-card>
        Total price is {{ totalSum | currency:'USD':'symbol-narrow':'1.2-2'}}
      </mat-card>
    </mat-card-content>

2- 及以下在我的 TS 文件中:

export class CreateSubProjectComponent extends AppComponentBase implements OnInit, AfterViewInit, OnDestroy {
 exampleForm: FormGroup;
  myFormValueChanges$;
  totalSum: number = 0;

constructor(injector: Injector,
private formBuilder: FormBuilder,
    private currencyPipe: CurrencyPipe){
super(injector);
}

  ngOnInit() {
this.exampleForm = this.formBuilder.group({
      units: this.formBuilder.array([

         this.getUnit()
      ])
    });

// initialize stream on units
    this.myFormValueChanges$ = this.exampleForm.controls['units'].valueChanges;
// subscribe to the stream so listen to changes on units
this.myFormValueChanges$.subscribe(units => this.updateTotalUnitPrice(units));

}//end of ngOnInit

ngAfterViewInit() {}
 ngOnDestroy() { this.myFormValueChanges$.unsubscribe(); }

private getUnit() {
    const numberPatern = '^[0-9.,]+$';
    return this.formBuilder.group({
      unitName: ['', Validators.required],
      qty: [1, [Validators.required, Validators.pattern(numberPatern)]],
      unitPrice: ['', [Validators.required, Validators.pattern(numberPatern)]],
      unitTotalPrice: [{value: '', disabled: true}]
    });
  }
/**
   * Add new unit row into form
   */
  addUnit() {
    const control = <FormArray>this.exampleForm.controls['units'];
    control.push(this.getUnit());
  }
  /**
   * Remove unit row from form on click delete button
   */
  removeUnit(i: number) {
    const control = <FormArray>this.exampleForm.controls['units'];
    control.removeAt(i);
  }
  /**
   * This is one of the way how clear units fields.
   */
  clearAllUnits() {
    const control = <FormArray>this.exampleForm.controls['units'];
    while(control.length) {
      control.removeAt(control.length - 1);
    }
    control.clearValidators();
    control.push(this.getUnit());
  }
 /**
   * Update prices as soon as something changed on units group
   */
  private updateTotalUnitPrice(units: any) {
    // get our units group controll
    const control = <FormArray>this.exampleForm.controls['units'];
    // before recount total price need to be reset. 
    this.totalSum = 0;
    for (let i in units) {
      let totalUnitPrice = (units[i].qty*units[i].unitPrice);
      // now format total price with angular currency pipe
      let totalUnitPriceFormatted = this.currencyPipe.transform(totalUnitPrice, 'USD', 'symbol-narrow', '1.2-2');
      // update total sum field on unit and do not emit event myFormValueChanges$ in this case on units
      control.at(+i).get('unitTotalPrice').setValue(totalUnitPriceFormatted, {onlySelf: true, emitEvent: false});
      // update total price for all units
      this.totalSum += totalUnitPrice;
    }
  }
}

这篇用于反应形式的文章也接近您的场景

于 2020-04-03T18:10:22.577 回答