Смешивание реактивной формы с шаблонной формой

#angular #angular-reactive-forms #angular-template-form

#angular #angular-реактивные формы #angular-template-form

Вопрос:

Я создал большую форму с большим количеством входных данных, используя шаблонную форму. Теперь у меня есть требование динамически добавлять часть ввода. Поскольку добавление входных данных динамически кажется проще с реактивной формой, я хотел бы изменить эту конкретную часть входных данных на реактивную форму.

Так можно ли смешивать реактивные формы и шаблонные формы в одном теге формы?

Комментарии:

1. да, это возможно, но я не уверен в его правильном или неправильном подходе.

2. @Abhishek Я погуглил, но не нашел ни одной статьи об этом.

3. Я бы сказал, быстро окунитесь и проверьте это, и, прежде чем вы это узнаете, вы переключитесь на реактивный. В любом случае она продается как решение для более сложных требований к форме. вы можете найти несколько причин здесь: почему смешивать два не рекомендуется: blog.angular-university.io /…

4. @suhailvs я заметил, что вы назначили награду. Не могли бы вы предоставить мне более подробную информацию, чтобы я мог предложить вам решение для этого?

5. @wentjun я использую angular 6.0.3, но я мог бы обновить до 7.x.x позже. Поэтому я думаю, что будет лучше придерживаться реактивных форм и удалить из него ngModels

Ответ №1:

Вы можете смешивать как реактивные формы, так и формы, управляемые шаблоном, но это настоятельно не рекомендуется. Это связано с тем, что использование ngModel в реактивных формах противоречит идее неизменности состояния формы.

Принципы реактивных форм соответствуют правилу «односторонней» привязки данных, в соответствии с которым вы следуете неизменному методу управления состоянием ваших форм, так что существует большее разделение интересов между вашим шаблоном и логикой компонента. Вы можете прочитать больше о преимуществах реактивных форм по ссылке в первом абзаце.

Предполагая, что вы продолжаете смешивать формы, управляемые шаблоном, и реактивные формы. При запуске консоль выдаст следующую ошибку ng serve :

Похоже, вы используете ngModel в том же поле формы, что и formControlName. Поддержка использования свойства ввода ngModel и события ngModelChange с директивами реактивной формы устарела в Angular v6 и будет удалена в Angular v7 Для получения дополнительной информации об этом см. Наши документы по API здесь: https://angular.io/api/forms/FormControlName#use-with-ngmodel

Ответ №2:

Выдержка из ссылки, которую я разместил выше / https://blog.angular-university.io/introduction-to-angular-2-forms-template-driven-vs-model-driven /

Раздел: Но что случилось с ngModel?

Обратите внимание, что ngModel все еще можно использовать с реактивными формами. Просто значение формы будет доступно в двух разных местах: модели представления и FormGroup, что потенциально может привести к некоторой путанице.

Ответ №3:

Да, вы можете использовать оба варианта вместе, сначала создать реактивную форму, а затем добавить шаблон, основанный на ваших требованиях, чтобы он работал.Пожалуйста, обратитесь к документации angular о том, как их можно использовать вместе

Комментарии:

1. ОШИБКА Ошибка: » ngModel нельзя использовать для регистрации элементов управления формой с помощью директивы родительской FormGroup. Попробуйте вместо этого использовать партнерскую директиву FormGroup «formControlName». Пример:

Ответ №4:

Да, вы можете, проверьте эту ссылку, это полная реактивная форма, но она близка к вашему сценарию, но вам нужно внести некоторые изменения, чтобы они соответствовали вашему случаю. В моем случае я сделал следующее :

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 amp;amp; 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;
}
}
}
эта статья для реактивной формы также близка к вашему сценарию