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:
- Initialize the
ingredientsFormArray reference upfront
Right now, you only assignthis.ingredientswhenaddIngredient()is called, but your HTML template tries to accessingredients.controlsimmediately 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; }
- 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 youraddIngredient()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>
- Optional: Add validation to ingredient fields
If you want to ensure users fill out each ingredient's name and amount, add validation rules to yourcreateIngredient()method:
createIngredient(): FormGroup { return this.fb.group({ name: ['', Validators.required], amount: ['', Validators.required] }); }
- Implement the missing
cancel()method
Your template has a Cancel button that callscancel(), 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'); }
- Improve error handling
Right now, you're assigning the raw error object tovalidationErrors, 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
相关产品推荐
相关产品推荐

