Home > Software design >  select option independent in formArray Angular
select option independent in formArray Angular

Time:12-08

Good evening, I have a problem I am using a reactive form, with FormArray, I have a button to add 2 options as many times as necessary, the problem is that the options are matselects and in one of these selects I use an event (change), the problem is that adding the options for the first time the onchange works normal, but when adding the options more than 2 times the values that were previously selected in the matselects change because the onchange of the created select is re-executed. I would like to know if there is a way to use that event independently in each iteration of the formArray

Component.ts

alicuotaForm: FormGroup;
  value = this.fb.group({
    manzana: ['', Validators.required],
    villa: ['', Validators.required],
  });
  nregistros: number;

 ngOnInit() {
    this.alicuotaForm = this.fb.group({
      times: this.fb.array([]),
    });
  }

//event onchange
  getVillas(value) {
    console.log('valor: ', value.value);
    this.filtro = this.villas.filter((x) => x.manzana == value.value);
  }
  addGroup() {
    const val = this.fb.group({
      manzana: ['', Validators.required],
      villa: ['', Validators.required],
    });
    const alicuotaForm = this.alicuotaForm.get('times') as FormArray;
    alicuotaForm.push(val);
    this.nregistros = alicuotaForm.length;
  }

  removeGroup(index) {
    const alicuotaForm = this.alicuotaForm.get('times') as FormArray;
    alicuotaForm.removeAt(index);
  }

  trackByFn(index: any, item: any) {
    return index;
  }

component.html

<div class="container">
  <div class="row">
    <div class="col-col-6">
      <label class="transparente"></label>
      <button (click)="addGroup()" type="button" class="btn btn-success">
        Agregar Campos
      </button>
    </div>
    <br />
    <form [formGroup]="alicuotaForm">
      <ng-container
        formArrayName="times"
        *ngFor="
          let time of alicuotaForm.controls.times?.value;
          let i = index;
          trackBy: trackByFn
        "
      >
        <ng-container [formGroupName]="i" style="margin-bottom: 10px;">
          Iteracion {{ i   1 }}
          <div class="col-col-6">
            <label>Manzana</label>
            <select
              formControlName="manzana"
              class="form-control custom-select"
              (change)="getVillas($event.target)"
            >
              >
              <option *ngFor="let ur of manzanas">
                {{ ur.manzana }}
              </option>
            </select>
          </div>
          <br />

          <div class="col-sm">
            <label>Villa</label>
            <select formControlName="villa" class="form-control custom-select">
              <option *ngFor="let ur of filtro" value="{{ ur.villa }}">
                {{ ur.villa }}
              </option>
            </select>
          </div>
          <br />
          ------------------------------------------------------------------------<br /> </ng-container
      ></ng-container>
    </form>
  </div>
</div>

I don't know what I could do or what would you recommend me to solve my problem, thank you very much

code in stackblitz Stackblitz

CodePudding user response:

You have to create the separate value for filtro you can get help from the following code

HTML

<hello name="{{ name }}"></hello>
<p>Start editing to see some magic happen :)</p>
<div >
 <div >
<div >
  <label ></label>
  <button (click)="addGroup()" type="button" >
    Agregar Campos filtro
  </button>
</div>
<br />
<form [formGroup]="alicuotaForm">
  <ng-container
    formArrayName="times"
    *ngFor="
      let time of alicuotaForm.controls.times?.value;
      let i = index;
      trackBy: trackByFn
    "
  >
    <ng-container [formGroupName]="i" style="margin-bottom: 10px;">
      Iteracion {{ i   1 }}
      <div >
        <label>Manzana</label>
        <select
          formControlName="manzana"
          
          (change)="getVillas($event.target, i)"
        >
          >
          <option *ngFor="let ur of manzanas">
            {{ ur.manzana }}
          </option>
        </select>
      </div>
      <br />

      <div >
        <label>Villa</label>
        <select formControlName="villa" >
          <option *ngFor="let ur of filtro[i]" value="{{ ur.villa }}">
            {{ ur.villa }}
          </option>
        </select>
      </div>
      <br />
      ------------------------------------------------------------------------<br /> </ng-container
  ></ng-container>
</form>

Typescript

  import { Component, VERSION } from '@angular/core';
  import {
    FormControl,
    FormGroup,
    FormBuilder,
    FormArray,
    Validators,
    FormGroupDirective,
 } from '@angular/forms';

  @Component({
    selector: 'my-app',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css'],
  })
 export class AppComponent {
name = 'Angular '   VERSION.major;
villas = [
  { manzana: '1', villa: '10' },
  { manzana: '1', villa: '20' },
  { manzana: '1', villa: '30' },
  { manzana: '2', villa: '40' },
  { manzana: '2', villa: '50' },
  { manzana: '2', villa: '60' },
  { manzana: '3', villa: '70' },
  { manzana: '3', villa: '80' },
  { manzana: '3', villa: '90' },
];
filtro: any[]=[];
manzanas = [{ manzana: '1' }, { manzana: '2' }, { manzana: '3' }];
alicuotaForm: FormGroup;
value = this.fb.group({
  manzana: ['', Validators.required],
  villa: ['', Validators.required],
});
nregistros: number;

constructor(private fb: FormBuilder) {}

ngOnInit() {
  this.alicuotaForm = this.fb.group({
    times: this.fb.array([]),
  });
}
getVillas(value,index) {
  console.log('valor: ', value.value);
  this.filtro[index] = this.filtro[index].filter(x => x.manzana === value.value);
}
addGroup() {
  const val = this.fb.group({
    manzana: ['', Validators.required],
    villa: ['', Validators.required],
  });
  
  const alicuotaForm = this.alicuotaForm.get('times') as FormArray;
  alicuotaForm.push(val);
  this.nregistros = alicuotaForm.length;
  this.filtro.push(null);
  this.filtro[this.nregistros-1]=[...this.villas];
}

removeGroup(index) {
  const alicuotaForm = this.alicuotaForm.get('times') as FormArray;
  alicuotaForm.removeAt(index);
}

trackByFn(index: any, item: any) {
  return index;
}
  }

CodePudding user response:

From what I can tell, your main problem is that you have a single variable, filtro, that you're trying to use for all of the elements in the FormArray. You can either:

  1. (My recommended) Create a custom Pure pipe to return a list of filtered villas. Pure pipes will only execute when the value changes, instead of every paint cycle.
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({ name: 'arrayFilter' })
export class ArrayFilterPipe implements PipeTransform {
  transform(arr: any[], property: string, value: any): any {
    return arr.filter((x) => x[property] === value);
  }
}

You then use it in your template like so (Don't forget to declare it in your module):

<select formControlName="villa" class="form-control custom-select">
  <option
    *ngFor="let ur of (villas | arrayFilter: 'manzana': time.manzana)"
    value="{{ ur.villa }}"
  >
    {{ ur.villa }}
  </option>
</select>

Typescript:

addGroup() {
    // either provide a '' option to this.manzanas, or set manzana: '1' to avoid expression after checked errors
    const val = this.fb.group({
      manzana: ['1', Validators.required],
      villa: ['', Validators.required],
    });
    const alicuotaForm = this.alicuotaForm.get('times') as FormArray;
    alicuotaForm.push(val);
    this.nregistros = alicuotaForm.length;
  }

For more information, see Pipes.

  1. Create another form control on your group called filtro, and you set its value in the getVillas function. You would then reference this form control's value when you do the *ngFor for the form control villa
  2. Create a function in your component that filters villas based on the array item's manzana (don't recommend, since this would recalculate every paint cycle).
  • Related