角度错误:无法读取 null 的属性(读取“控件”)

Posted

技术标签:

【中文标题】角度错误:无法读取 null 的属性(读取“控件”)【英文标题】:Angular Error: Cannot read properties of null (reading 'controls') 【发布时间】:2022-01-18 10:30:15 【问题描述】:

我目前正在尝试了解 FormArrays、FormGroups 和 FormControls 的工作原理。我想以以下格式将数据插入到我的 firebase 集合中。

Firebase Format

我的代码编译成功,但我在控制台中收到一个错误,显示 core.js:6486 ERROR TypeError: Cannot read properties of null (reading 'controls') 在 AddRecipeComponent_Template (add-recipe.component.html:56)。有什么我缺少的东西,比如初始化?

我的模型如下所示: 食谱.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'];

add-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>

add-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);
  


【问题讨论】:

【参考方案1】:

你的getter中的FormArray路径是错误的,你的formarray是inside recipeDetails formgroup,所以这是你需要指向的地方:

get ingredients(): FormArray 
  return <FormArray>this.addRecipeForm.get('recipeDetails.ingredients');

【讨论】:

以上是关于角度错误:无法读取 null 的属性(读取“控件”)的主要内容,如果未能解决你的问题,请参考以下文章

角度错误无法读取未定义的属性(读取“init”)

角度ngrx存储错误无法读取未定义的属性“计划”

错误类型错误:无法读取未定义角度的属性“名称”

未捕获的类型错误:无法读取 null 的属性(读取“添加”)

由于无法读取未定义角度的属性“键”而出现错误

错误类型错误:无法在styles.css 中读取null 的属性(读取'classList')