angularcascadingdropdownformarraycascading

cascading dropdown in dynamic row angular?


I have cascading dropdown list which works alone, but when I generate a new row and change one of my dropdowns its affects my entire dropdown list.

Here is my html code.

    <td>       
       <select class="form-control" formControlName="province_id" 
           (change)="onChangeProvince($event)">
          <option >Select Province</option>
          <option *ngFor="let province of provinces; let i = index" 
              [value]="province.id">{{province.province_name}}</option>
       </select>
    </td>
    <td>
       <select  class="form-control" formControlName="district_id">
          <option >Select District</option>
          <option *ngFor="let district of districts" [value]="district.id">
               {{district.district_name}}</option>
       </select>
    </td>
    <td>
       <button class="btn btn-danger"  type="button" 
           (click)="deleteMyRelation(i)">Delete</button>
       <button class="btn btn-primary" type="button" 
           (click)="addItem()">Add</button>
    </td>

Here is my component code.

      createItem(): FormGroup {
            return this.fb.group({
              province_id: '',
              district_id: ''
            });
          }
        
       addItem(): void {
            this.itemRows = this.contactForm.get('itemRows') as FormArray;
            this.itemRows.push(this.createItem());
          }

       ngOnInit() {
            this.contactForm = this.fb.group({
              province_id: '',
              district_id: '',
              itemRows: this.fb.array([ this.createItem() ])
            });
            this.getProvinces();
          }

          provinces = <any>[];
          districts = <any>[];

          getProvinces(){
            this.contactService.getProvinces().subscribe(
              data => {
                this.provinces = data;
                console.log(data);
              },
              error => {
                console.log(error);
              });
          }
          onChangeProvince(event:any){
            
            this.contactService.getDistrict(event.target.value).subscribe(
              data => {
                this.districts = data;
              },
              error => {
                console.log(error);
              });
          }

Solution

  • You need define each control with a [formGroupName]='i'. Now Angular will assign the values to each group properly, something like

    <table [formGroup]='contactForm'>
      <ng-container formArrayName='itemRows'>
        <tr *ngFor="let itemRow of itemRows.controls; let i = index" [formGroupName]='i'>
          <td>
            <select class="form-control" formControlName="province_id"
               (change)="onChangeProvince(i)">
              <option  value=''>Select Province</option>
              <option *ngFor="let province of provinces; let i = index" 
                  [value]="province.id">{{province.province_name}}</option>
           </select>
          </td>
          <td>
            <select  class="form-control" formControlName="district_id">
              <option value=''>Select District</option>
              <option *ngFor="let district of districts[i]" [value]="district.id">
                   {{district.district_name}}</option>
           </select>
          </td>
          <td>
            <button class="btn btn-danger"  type="button"
               (click)="deleteMyRelation(i)">Delete</button>
            <button class="btn btn-primary" type="button"
               (click)="addItem()">Add</button>
          </td>
        </tr>
      </ng-container>
    </table>
    

    We can now define below functions to enable adding and removing of items

      get itemRows() {
        return this.contactForm.get("itemRows") as FormArray;
      }
      deleteMyRelation(i) {
        this.itemRows.removeAt(i)
      }
      addItem(): void {
        this.itemRows.push(this.createItem());
      }
      onChangeProvince(i) {
        this.contactService
          .getDistrict(this.itemRows.get(i + ".province_id").value)
          .subscribe(
            data => {
              this.districts[i] = data;
            },
            error => {
              console.log(error);
            }
          );
      }
    

    Now your form should work as expected

    See Demo