SBB
SBB

Reputation: 8970

Angular reactive form hidden input not binding?

I have a reactive form where I create the controls from my data model. Initially, everything is sorted by a datapoint called the "processingOrder" in numerical order.

Within my form array, I am using *ngFor to iterate over controls and store the index in a hidden form control. If I move a record up or down in my table, the index thats being applied to the hidden field should reflect the change in my model right?

<form [formGroup]="rulesForm" (ngSubmit)="onSubmit(form)">
<div formGroupName="ruleData">
   <div formArrayName="rules">
      <div *ngFor="let child of rulesForm.controls.ruleData.controls.rules.controls; let i = index">
         <div formGroupName="{{i}}">
            <input type="text" placeholder="Rule Name" formControlName="name"/> &nbsp; 
            <input type="text" placeholder="Rule Description" formControlName="description"/> &nbsp;
            <input type="text" placeholder="Processing Order" formControlName="processingOrder"/> &nbsp;
            <button class="button" (click)="move(-1, i)">Move Up</button> &nbsp; 
            <button class="button" (click)="move(1, i)">Move Down</button>


            <!-- Why doesn't this update my model when it gets a new index? -->
            <input type="hidden" formControlName="processingOrder" [value]="i">


         </div>
      </div>
   </div>
</div>
<button type="submit">Submit</button>
</form>

I would have expected that in my plunker, the processing order numbers should always remain in the 1-5 order and each time a rule is moved up or down, the model is updated to the new index it received.

https://plnkr.co/edit/ZCgHPEaUM00aLxM6Sf9t?p=preview

Upvotes: 13

Views: 55070

Answers (2)

Andriy
Andriy

Reputation: 15442

formControlName directive has ngModel input which is bound to control's model and when changed from the code will update all its instances on view. So, just replace [value]="i" with [ngModel]="i + 1":

<input type="hidden" formControlName="processingOrder" [ngModel]="i + 1">

binding to HTML input's property value ([value]="i + 1") will update current input on the view but won't update control's model, thus won't affect another instances with the same control name.

You also can remove hidden input and place [value]="i + 1" on the text input:

<input type="text" 
       placeholder="Processing Order" 
       [ngModel]="i + 1"
       formControlName="processingOrder"/>

please note that processingOrder value will always be overridden by ngFor's index i

2021 UPDATE :) be aware of ngModel (with reactive form directives) deprecation since NG6:

Support for using the ngModel input property and ngModelChange event with reactive form directives has been deprecated in Angular v6 and is scheduled for removal in a future version of Angular.

Upvotes: 28

amal
amal

Reputation: 3170

Add this to the bottom of the move up/down handling method,

move(shift, currentIndex) {
  const rules = this.rulesForm.get(['ruleData', 'rules']);

  let newIndex: number = currentIndex + shift;
  if(newIndex === -1) {
    newIndex = rules.length - 1;
  } else if(newIndex == rules.length) {
    newIndex = 0;
  }

  const currentGroup = rules.at(currentIndex);
  rules.removeAt(currentIndex);;
  rules.insert(newIndex, currentGroup)

  // logic to re-calculate processingOrder in the correct ascending order
  let i = 0;
  this.rulesForm.get(['ruleData', 'rules']).controls.forEach((elem) => {
    i++;
    elem.get('processingOrder').setValue(i);
  });
}

It basically re-assigns the processingOrder numbers in ascending order based on the formArray length. Hope it helps. (Confirmed it working in plunker)

You don't need the hidden input in the template for this to work. Also, if you specify more than one input controls (here input elements of type 'text' and 'hidden' for 'processingOrder') with the same formControlName then it probably is not gonna work as you expect in a reactive form.

Upvotes: -2

Related Questions