Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I have a stackblitz as a guide.

I am wanting to display a list of material cards that I click an 'edit' button, to which I can edit text fields, and when I click on the 'save' icon, it of course saves by triggering an function etc.

I am struggling however to get to grips with how this all works within Angular and the Material nature of my app.

html

<form id="myForm" [formGroup]="thisIsMyForm">
  <mat-card [formGroup]="x" *ngFor="let x of data; let i = index">
    <mat-form-field>
      <label for="{{x.name}}">Name</label>
      <input formControlName="name" id="{{x.name}}" matInput value="{{x.name}}">
    </mat-form-field>
    <mat-form-field>
      <label for="{{x.type}}">Type</label>
      <input formControlName="type" id="{{x.type}}" matInput value="{{x.type}}"/>
    </mat-form-field>
  </mat-card>
</form>

ts

import { Component, ViewChild } from '@angular/core';
import {MatSnackBar} from '@angular/material';
import {FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  thisIsMyForm: FormGroup

  data = [
    {name:"one", type:"one"},
    {name:"two", type:"two"},
    {name:"three", type:"three"},
  ];

  constructor(private formBuilder: FormBuilder) {}

  onSubmit() {
    // Here I would like to be able to access the values of the 'forms'
  }

}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
377 views
Welcome To Ask or Share your Answers For Others

1 Answer

You are diving into the deep end for sure, trying to build a dynamic reactive form within the scope of an *ngFor is a challenge. I will walk you through it the best I can.

You will need to create an array for controls, in your constructor create your form setting formArrayName as an empty array using this.formBuild.array([])... call this whatever you want, I just used formArrayName for demonstration purposes.

After the form is instantiated with an empty array call this.buildForm()

constructor(private formBuilder: FormBuilder) {
    this.thisIsMyForm = new FormGroup({
      formArrayName: this.formBuilder.array([])
    })

    this.buildForm();
  }

In your buildForm() iterate over your data[] and push controls for each index while assigning the default value and a default state of disabled.

 buildForm() {
    const controlArray = this.thisIsMyForm.get('formArrayName') as FormArray;

    Object.keys(this.data).forEach((i) => {
      controlArray.push(
        this.formBuilder.group({
          name: new FormControl({ value: this.data[i].name, disabled: true }),
          type: new FormControl({ value: this.data[i].type, disabled: true })
        })
      )
    })

    console.log(controlArray)
  }

Please Note: console.log(controlArray.controls) results in the following output... each index is a FormGroup with two controls name and type

0: FormGroup
1: FormGroup
2: FormGroup

In your html you will need to establish a container hierarchy that mimics the thisIsMyForm you just created.

  • parent:thisIsMyForm
  • child:formArrayName
  • grandchild:i as formGroupName

grandchild is important because it matches the console log of controlArray.controls in previous step

<form id="myForm" [formGroup]="thisIsMyForm">
    <div [formArrayName]="'formArrayName'">
        <mat-card *ngFor="let x of data; let i = index">
            <div [formGroupName]="i">

Create edit and save buttons based on control disabled state

   <button *ngIf="formControlState(i)" (click)="toggleEdit(i)">Enable Edit</button>
    <button *ngIf="!formControlState(i)" (click)="toggleEdit(i)">Save</button>

Create methods in component to receive the index as an argument and handle logic to hide buttons and toggle input fields enable and disable state.

toggleEdit(i) {
    const controlArray = this.thisIsMyForm.get('formArrayName') as FormArray;
    if(controlArray.controls[i].status === 'DISABLED'){
      controlArray.controls[i].enable()
    }else{
      controlArray.controls[i].disable()
    }
  }

  formControlState(i){
     const controlArray = this.thisIsMyForm.get('formArrayName') as FormArray;
     return controlArray.controls[i].disabled
  }

Submit button that console.log's the form value when clicked... also disable button while any of the input formControls are in enabled state.

<button [disabled]="thisIsMyForm.get('formArrayName').enabled" (click)="onSubmit()">Submit Form</button>

onSubmit() {
    // Here I would like to be able to access the values of the 'forms'
    console.log(this.thisIsMyForm.value)
  }

Stackblitz

https://stackblitz.com/edit/dynamic-form-ngfor-otbuzn?embed=1&file=src/app/app.component.ts


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...