You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular 10中如何在FormGroup内使用FormArray?表单开发求助

Hey there! First off, great job diving into FormArray—it's super handy for dynamic form fields, so kudos for taking that step. Let's walk through the issues in your code and get your recipe form working smoothly.

Key Issues & Fixes

Let's break down what's causing the block and how to fix each part:

  1. Initialize the ingredients FormArray reference upfront
    Right now, you only assign this.ingredients when addIngredient() is called, but your HTML template tries to access ingredients.controls immediately on load. This will throw an "undefined" error. Fix this by setting the reference when you initialize the form:
// First, fix the method name spelling (optional but more standard)
initializeRecipeForm() { 
  this.recipeForm = this.fb.group({
    name: ['', Validators.required],
    preparationTime: ['', Validators.required],
    description: ['', Validators.required],
    ingredients: this.fb.array([this.createIngredient()])
  });
  // Assign the FormArray reference right away
  this.ingredients = this.recipeForm.get('ingredients') as FormArray;
}
  1. Add a button to trigger adding new ingredients
    Your template doesn't have a way for users to actually add new ingredient fields! Add a button below the ingredient inputs to call your addIngredient() method:
<div class="form-group">
  <ng-container formArrayName="ingredients">
    <div *ngFor="let ingredient of ingredients.controls; index as i" class="mb-3">
      <ng-container [formGroupName]="i">
        <input formControlName="name" class="form-control mb-1" placeholder="Ingredient name"/>
        <input formControlName="amount" class="form-control" placeholder="Amount (e.g., 1 cup)"/>
      </ng-container>
    </div>
  </ng-container>
  <!-- Add this button to add new ingredients -->
  <button type="button" class="btn btn-outline-primary mt-2" (click)="addIngredient()">Add New Ingredient</button>
</div>
  1. Optional: Add validation to ingredient fields
    If you want to ensure users fill out each ingredient's name and amount, add validation rules to your createIngredient() method:
createIngredient(): FormGroup {
  return this.fb.group({
    name: ['', Validators.required],
    amount: ['', Validators.required]
  });
}
  1. Implement the missing cancel() method
    Your template has a Cancel button that calls cancel(), but the method doesn't exist in your component. Add it to handle navigation back to the recipes list:
cancel(): void {
  this.router.navigateByUrl('/recipes');
}
  1. Improve error handling
    Right now, you're assigning the raw error object to validationErrors, which won't display correctly. Adjust the error callback to extract meaningful error messages:
error => {
  // Adjust this based on your backend's error response structure
  this.validationErrors = error.error?.errors 
    ? Object.values(error.error.errors).flat() 
    : [error.message || 'Something went wrong'];
}

Full Modified Component Code

export class RecipeAddComponent implements OnInit { 
  recipe: IRecipe; 
  recipeForm: FormGroup; 
  ingredients: FormArray; 
  ingredientForm: FormGroup; 
  validationErrors: string[] = []; 

  constructor(
    private recipeService: RecipeService, 
    private fb: FormBuilder, 
    private router: Router
  ) { } 

  ngOnInit(): void { 
    this.initializeRecipeForm(); 
  } 

  initializeRecipeForm() { 
    this.recipeForm = this.fb.group({
      name: ['', Validators.required],
      preparationTime: ['', Validators.required],
      description: ['', Validators.required],
      ingredients: this.fb.array([this.createIngredient()])
    });
    this.ingredients = this.recipeForm.get('ingredients') as FormArray;
  } 

  createIngredient(): FormGroup { 
    return this.fb.group({
      name: ['', Validators.required],
      amount: ['', Validators.required]
    }); 
  } 

  addRecipe() { 
    this.recipeService.addNewRecipe(this.recipeForm.value).subscribe(
      response => { this.router.navigateByUrl('/recipes'); },
      error => {
        this.validationErrors = error.error?.errors 
          ? Object.values(error.error.errors).flat() 
          : [error.message || 'Something went wrong'];
      }
    );
  } 

  addIngredient(): void { 
    this.ingredients.push(this.createIngredient()); 
  }

  cancel(): void {
    this.router.navigateByUrl('/recipes');
  }
}

Full Modified HTML Template

<div class="container"> 
  <form class="p-5 col-md-5 col-sm-8 formShadow" [formGroup]='recipeForm' (ngSubmit)="recipeForm.valid && addRecipe()" autocomplete="off"> 
    <p class="h4 mb-4">Add new recipe</p> 

    <app-text-input [formControl]='recipeForm.controls["name"]' [label]='"Name"'></app-text-input> 
    <app-text-input [formControl]='recipeForm.controls["preparationTime"]' [label]='"Preparation Time"' [type]='"number"'></app-text-input> 
    <app-text-input [formControl]='recipeForm.controls["description"]' [label]='"Description"'></app-text-input> 

    <div class="form-group mt-4">
      <h6 class="mb-2">Ingredients</h6>
      <ng-container formArrayName="ingredients">
        <div *ngFor="let ingredient of ingredients.controls; index as i" class="mb-3">
          <ng-container [formGroupName]="i">
            <input formControlName="name" class="form-control mb-1" placeholder="Ingredient name"/>
            <input formControlName="amount" class="form-control" placeholder="Amount (e.g., 1 cup)"/>
          </ng-container>
        </div>
      </ng-container>
      <button type="button" class="btn btn-outline-primary mt-2" (click)="addIngredient()">Add New Ingredient</button>
    </div>

    <div class="row mt-5" *ngIf="validationErrors.length > 0"> 
      <ul class="text-danger"> 
        <li *ngFor="let error of validationErrors">{{error}}</li> 
      </ul> 
    </div> 

    <div class="form-group text-center mt-4"> 
      <button [disabled]="!recipeForm.valid" class="btn btn-outline-dark mr-2" type="submit">Save Recipe</button> 
      <button class="btn btn-outline-light mr-2" (click)="cancel()">Cancel</button> 
    </div> 
  </form> 
</div>

These changes should get your dynamic ingredient form working as expected. Let me know if you run into any other snags!

内容的提问来源于stack exchange,提问作者user12464072

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 15:42:38