Angular Error: Cannot read properties of null (reading 'controls')
Angular Error: Cannot read properties of null (reading 'controls')
我目前正在尝试了解 FormArrays、FormGroups 和 FormControls 的工作原理。我想按以下格式将数据插入到我的 firebase 集合中。
Firebase Format
我的代码编译成功,但我在控制台中收到一条错误消息 core.js:6486 ERROR TypeError: Cannot read properties of null (reading 'controls')
在 AddRecipeComponent_Template(添加-recipe.component.html:56)。有没有我遗漏的东西,比如初始化?
我的模型是这样的:
Recipe.ts
export interface Recipe {
id: string;
metaData: {
name: string;
img: string;
description: string;
viewed: number;
};
recipeDetails: {
ingredients: Ingredients[],
instructions: string,
cookingTime: string,
servingPortion: string,
dietaryInformation: string
}
}
export interface Ingredients {
name: string,
amount: number,
unit: string
}
export type RecommendedRecipe = { id: string } & Recipe['metaData'];
export type RecipeDetail = { id: string } & Recipe['metaData'] & Recipe['recipeDetails'];
加-recipe.component.html
<div class="container mb-5">
<div class="row">
<form [formGroup]="addRecipeForm" class="row g-3">
<div formGroupName="metaData">
<div class="col-md-6">
<label for="name"> Recipe Name </label>
<input type="text" name="name" class="form-control" formControlName="name" />
</div>
<div class="col-md-6">
<label for="img"> Img </label>
<input type="text" name="img" class="form-control" formControlName="img" />
</div>
<div class="col-md-6">
<label for="description"> Description </label>
<input type="text" name="description" class="form-control" formControlName="description" />
</div>
<div class="col-md-2">
<input type="number" name="viewed" class="form-control" formControlName="viewed" value=1 hidden />
</div>
</div>
<div formGroupName="recipeDetails">
<div class="col-md-6">
<label for="cookingTime"> Cooking Time </label>
<input type="text" name="cookingTime" class="form-control" formControlName="cookingTime" />
</div>
<div class="col-md-6">
<label for="servingPortion"> Serving Portion </label>
<input type="text" name="servingPortion" class="form-control" formControlName="servingPortion" />
</div>
<div class="col-md-6">
<label for="dietaryInformation"> Dietary Information </label>
<input type="text" name="dietaryInformation" class="form-control" formControlName="dietaryInformation" />
</div>
<div class="col-md-6">
<label for="instructions"> Instructions </label>
<textarea class="form-control" name="instructions" rows="20" style="resize: none;"
formControlName="instructions"></textarea>
</div>
<br/>
<div class="col-md-2 d-md-flex justify-content-md-end">
<button class="btn btn-primary" (click)="addIngredients()"> Add Ingredients </button>
</div>
<br/>
<!--Ingredients-->
<div formArrayName="ingredients" *ngFor="let ing of ingredients.controls; let i = index">
<div [formGroupName]="i">
<div class="col-md-2">
<label for="name"> Ingredients Name </label>
<input type="text" name="name" class="form-control" formControlName="name" />
</div>
<div class="col-md-2">
<label for="amount"> Amount </label>
<input type="number" name="amount" class="form-control" formControlName="amount" />
</div>
<div class="col-md-2">
<label for="unit"> Unit </label>
<select name="unit" class="form-control" formControlName="unit">
<option value=""> Please Select Unit </option>
<option *ngFor="let unitOpt of unitOptions" [value]="unitOpt">{{ unitOpt }}</option>
</select>
</div>
<div class="col-md-6"></div>
<div class="col-md-6"></div>
<br />
</div>
</div>
</div>
<div class="col-md-6"></div>
<div class="col-md-6"></div>
<div class="col-md-2 gap-3 d-md-flex justify-content-md-end">
<button class="btn btn-primary" (click)="addRecipe()"> Add Recipe </button>
<a href="/recipe-list" class="btn btn-warning"> Cancel </a>
</div>
</form>
</div>
</div>
加-recipe.component.ts
import { Component, OnInit } from '@angular/core';
import { FormArray, FormControl, FormGroup, FormBuilder, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { Recipe } from 'src/app/models/Recipe';
import { LoadingService } from 'src/app/services/loading.service';
import { RecipeService } from 'src/app/services/recipe.service';
@Component({
selector: 'app-add-recipe',
templateUrl: './add-recipe.component.html',
styleUrls: ['./add-recipe.component.scss']
})
export class AddRecipeComponent implements OnInit {
addRecipeForm: FormGroup;
unitOptions: string[] = [
'Piece(s)',
'Slice(s)',
'Liter(s)',
'Milliliter(s)',
'Gram(s)',
'Kilogram(s)'
]
constructor(
private recipeService: RecipeService,
private loadingService: LoadingService,
private router: Router,
private fb: FormBuilder
) {
this.addRecipeForm = this.fb.group({
metaData: this.fb.group({
name: [''],
img: [''],
description: [''],
viewed: ['']
}),
recipeDetails: this.fb.group({
instructions: [''],
cookingTime: [''],
servingPortion: [''],
dietaryInformation: [''],
ingredients: this.fb.array([
this.addIngredientsFormGroup()
],Validators.required)
})
});
}
ngOnInit() {
}
public addIngredientsFormGroup(): FormGroup {
return this.fb.group({
name: [''],
amount: [''],
unit: ['']
})
}
get ingredients():FormArray{
return <FormArray> this.addRecipeForm.get('ingredients');
}
addIngredients() {
this.ingredients.push(this.addIngredientsFormGroup());
}
public addRecipe(): void {
// bind to Recipe Model
var newRecipe = {
metaData: this.addRecipeForm.value.metaData,
recipeDetails: this.addRecipeForm.value.recipeDetails
} as unknown as Recipe;
console.log('addRecipeForm -> ', newRecipe);
this.recipeService.createRecipe(newRecipe)
.subscribe(
(result) => {
console.log("add result", result);
this.router.navigateByUrl('/recipe-list');
}
);
}
onSubmit(): void {
console.log(this.addRecipeForm);
}
}
你的getter中的FormArray
路径是错误的,你的formarray是inside recipeDetails
formgroup,所以这就是你需要的地方指向:
get ingredients(): FormArray {
return <FormArray>this.addRecipeForm.get('recipeDetails.ingredients');
}
我目前正在尝试了解 FormArrays、FormGroups 和 FormControls 的工作原理。我想按以下格式将数据插入到我的 firebase 集合中。
Firebase Format
我的代码编译成功,但我在控制台中收到一条错误消息 core.js:6486 ERROR TypeError: Cannot read properties of null (reading 'controls') 在 AddRecipeComponent_Template(添加-recipe.component.html:56)。有没有我遗漏的东西,比如初始化?
我的模型是这样的: Recipe.ts
export interface Recipe {
id: string;
metaData: {
name: string;
img: string;
description: string;
viewed: number;
};
recipeDetails: {
ingredients: Ingredients[],
instructions: string,
cookingTime: string,
servingPortion: string,
dietaryInformation: string
}
}
export interface Ingredients {
name: string,
amount: number,
unit: string
}
export type RecommendedRecipe = { id: string } & Recipe['metaData'];
export type RecipeDetail = { id: string } & Recipe['metaData'] & Recipe['recipeDetails'];
加-recipe.component.html
<div class="container mb-5">
<div class="row">
<form [formGroup]="addRecipeForm" class="row g-3">
<div formGroupName="metaData">
<div class="col-md-6">
<label for="name"> Recipe Name </label>
<input type="text" name="name" class="form-control" formControlName="name" />
</div>
<div class="col-md-6">
<label for="img"> Img </label>
<input type="text" name="img" class="form-control" formControlName="img" />
</div>
<div class="col-md-6">
<label for="description"> Description </label>
<input type="text" name="description" class="form-control" formControlName="description" />
</div>
<div class="col-md-2">
<input type="number" name="viewed" class="form-control" formControlName="viewed" value=1 hidden />
</div>
</div>
<div formGroupName="recipeDetails">
<div class="col-md-6">
<label for="cookingTime"> Cooking Time </label>
<input type="text" name="cookingTime" class="form-control" formControlName="cookingTime" />
</div>
<div class="col-md-6">
<label for="servingPortion"> Serving Portion </label>
<input type="text" name="servingPortion" class="form-control" formControlName="servingPortion" />
</div>
<div class="col-md-6">
<label for="dietaryInformation"> Dietary Information </label>
<input type="text" name="dietaryInformation" class="form-control" formControlName="dietaryInformation" />
</div>
<div class="col-md-6">
<label for="instructions"> Instructions </label>
<textarea class="form-control" name="instructions" rows="20" style="resize: none;"
formControlName="instructions"></textarea>
</div>
<br/>
<div class="col-md-2 d-md-flex justify-content-md-end">
<button class="btn btn-primary" (click)="addIngredients()"> Add Ingredients </button>
</div>
<br/>
<!--Ingredients-->
<div formArrayName="ingredients" *ngFor="let ing of ingredients.controls; let i = index">
<div [formGroupName]="i">
<div class="col-md-2">
<label for="name"> Ingredients Name </label>
<input type="text" name="name" class="form-control" formControlName="name" />
</div>
<div class="col-md-2">
<label for="amount"> Amount </label>
<input type="number" name="amount" class="form-control" formControlName="amount" />
</div>
<div class="col-md-2">
<label for="unit"> Unit </label>
<select name="unit" class="form-control" formControlName="unit">
<option value=""> Please Select Unit </option>
<option *ngFor="let unitOpt of unitOptions" [value]="unitOpt">{{ unitOpt }}</option>
</select>
</div>
<div class="col-md-6"></div>
<div class="col-md-6"></div>
<br />
</div>
</div>
</div>
<div class="col-md-6"></div>
<div class="col-md-6"></div>
<div class="col-md-2 gap-3 d-md-flex justify-content-md-end">
<button class="btn btn-primary" (click)="addRecipe()"> Add Recipe </button>
<a href="/recipe-list" class="btn btn-warning"> Cancel </a>
</div>
</form>
</div>
</div>
加-recipe.component.ts
import { Component, OnInit } from '@angular/core';
import { FormArray, FormControl, FormGroup, FormBuilder, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { Recipe } from 'src/app/models/Recipe';
import { LoadingService } from 'src/app/services/loading.service';
import { RecipeService } from 'src/app/services/recipe.service';
@Component({
selector: 'app-add-recipe',
templateUrl: './add-recipe.component.html',
styleUrls: ['./add-recipe.component.scss']
})
export class AddRecipeComponent implements OnInit {
addRecipeForm: FormGroup;
unitOptions: string[] = [
'Piece(s)',
'Slice(s)',
'Liter(s)',
'Milliliter(s)',
'Gram(s)',
'Kilogram(s)'
]
constructor(
private recipeService: RecipeService,
private loadingService: LoadingService,
private router: Router,
private fb: FormBuilder
) {
this.addRecipeForm = this.fb.group({
metaData: this.fb.group({
name: [''],
img: [''],
description: [''],
viewed: ['']
}),
recipeDetails: this.fb.group({
instructions: [''],
cookingTime: [''],
servingPortion: [''],
dietaryInformation: [''],
ingredients: this.fb.array([
this.addIngredientsFormGroup()
],Validators.required)
})
});
}
ngOnInit() {
}
public addIngredientsFormGroup(): FormGroup {
return this.fb.group({
name: [''],
amount: [''],
unit: ['']
})
}
get ingredients():FormArray{
return <FormArray> this.addRecipeForm.get('ingredients');
}
addIngredients() {
this.ingredients.push(this.addIngredientsFormGroup());
}
public addRecipe(): void {
// bind to Recipe Model
var newRecipe = {
metaData: this.addRecipeForm.value.metaData,
recipeDetails: this.addRecipeForm.value.recipeDetails
} as unknown as Recipe;
console.log('addRecipeForm -> ', newRecipe);
this.recipeService.createRecipe(newRecipe)
.subscribe(
(result) => {
console.log("add result", result);
this.router.navigateByUrl('/recipe-list');
}
);
}
onSubmit(): void {
console.log(this.addRecipeForm);
}
}
你的getter中的FormArray
路径是错误的,你的formarray是inside recipeDetails
formgroup,所以这就是你需要的地方指向:
get ingredients(): FormArray {
return <FormArray>this.addRecipeForm.get('recipeDetails.ingredients');
}