Angular Reactive Form : How to preset values ? (FormArray containing objects)

Viewed 179

I'm trying to implement a classical "Update Object" feature.

I have a table with all my products listed, each line has an edit button opening a modal where I want to show product property values.

The property we focus on here is 'personas' and its value is an array of objects, each selected from a dropdown.

The form I'd like to prefill

I managed to show the right number of objects (3 in that example), but the values just don't set and the default option is selected in each case.

I tried to use angular 'patchValue()' function without success on that property.

Here is what I have as Typescript :

public personas = new FormArray([], [], []);

constructor(private fb: FormBuilder, public productService: ProductService) {
  this.productForm = this.fb.group({
      personas: this.personas,
    });
}

public onEdit(product: Product): void {
    if (product.personas.length > 0) {
     this.personaService.personas$.pipe(takeUntil(this._destroyed$)).subscribe((personas: Persona[]) => {
        const array = product.personas.filter(p => personas.some(p2 => p.id === p2.id));
        this.personas = this.fb.array(array.map(persona => new FormControl({
          id: persona.id,
          persona: persona.persona,
        })));
      });
    }
}

And this is what's in my template :

<div class="form-group">
  <label for="personas">Personas</label>
  <div id="personas" formArrayName="personas">
    <div *ngFor="let persona of personas.controls; let i = index;" class="d-flex m-1">
      <select
        *ngIf="!(personaService.isFetchingPersonas$ |async) && (personaService.personas$ |async) as options"
         class="form-control">
        <option *ngFor="let option of options" [ngValue]="option">{{ option.persona }}</option>
      </select>
  </div>
</div>

Thank you for your help

!!! EDIT !!!

I realized that while I tried to achieve my aim, I broke the process of creating new Products, as Persona property was not set correctly anymore.

So the Stackblitz project is slightely different from the above snippets. Because I moved back my project state to when it was functionnal.

STACKBLITZ : https://angular-ivy-cnmqhx.stackblitz.io

1 Answers

At the bottom of hello.component.html I added this little helper to see what is inside the form.

<pre>
{{ productForm.value | json }}
</pre>

then I changed ngOnInit to look like this:

  public ngOnInit(): void {
    this.product.personas.forEach((persona: Persona) => {
      const option = this.options.find(x => x.id === persona.id);
      if (option) {
        this.addField(option);
      }
    });
  }

The idea here is to lookup each "option" and put that option into formArray, instead of "persona".

also a bit simplified addField method

  public addField(persona = {}): void {
    (this.productForm.get("personas") as FormArray).push(
      new FormControl(persona)
    );
  }

Working Stackblitz

Related