Angular中Cannot read properties of null(writeValue)错误解决求助
Angular checkbox控件绑定导致TypeError问题的解决方案
问题描述
开发Angular车辆管理组件时,根据ID获取车辆对象,存在则编辑,不存在则注册。编辑场景下能正常获取对象,但抛出错误:TypeError: Cannot read properties of null (reading 'writeValue'),排查发现是checkbox控件导致,移除后错误消失,但业务需要保留该控件,其绑定布尔类型的veiculo.portariaSEJUSP属性。
环境信息:
- Angular CLI: 14.0.5
- Node: 16.14.2
- Package Manager: npm 8.5.0
- OS: win32 x64
原HTML代码
<div class="card shadow-sm m-5 p-5 bg-light" style="min-width: 60%; max-width: 70%"> <mat-toolbar>{{(veiculo && veiculo.id)?'Editar Veiculo':'Cadastrar Veiculo'}}</mat-toolbar> <div class="card-body"> <div class="form-group" *ngIf="veiculo.id != null"> <label class="form-label">ID</label> <input type="text" class="form-control" [(ngModel)]="veiculo.id" readonly /> </div> <div class="form-group"> <label class="form-label">Placa Oeste</label> <input type="text" class="form-control" [(ngModel)]="veiculo.placaOeste" /> </div> <div class="form-group"> <label class="form-label">Modelo</label> <input type="text" class="form-control" [(ngModel)]="veiculo.modelo"> </div> <div class="form-group"> <label class="form-label">Marca</label> <input type="text" class="form-control" [(ngModel)]="veiculo.marca"> </div> <div class="form-group"> <label class="form-label">Ano</label> <input type="text" class="form-control" [(ngModel)]="veiculo.ano"> </div> <div class="form-group form-check"> <input type="checkbox" class="form-check-input"> <label class="form-check-label" [(ngModel)]="veiculo.portariaSEJUSP">Deriva da Portaria SEJUSP/DGPC nº 133/2018</label> </div> <div> <button class="btn btn-primary m-2" (click)="salvar()"> Salvar </button> <button type="button" class="btn btn-warning mx-2" routerLink="/veiculos"> Cancelar </button> </div> </div> </div>
原TS代码
export class VeiculoCadastrarEditarComponent implements OnInit { veiculoForm!: FormGroup; veiculo = new Veiculo(); id: any = 0; constructor( private service: VeiculoService, private router: Router, private activatedRoute: ActivatedRoute ) {} ngOnInit(): void { this.id = this.activatedRoute.snapshot.paramMap.get('id'); if (this.id) { this.service.pesquisarPorId(this.id).subscribe({ next: (data) => (this.veiculo = data), error: (err) => console.log(err), }); } } salvar() { if (this.veiculo && this.veiculo.id) { this.service .atualizar(this.veiculoForm.value, this.veiculo.id) .subscribe({ next: () => this.router.navigateByUrl('/veiculos'), // error: () => this.onErrorEdicao(), }); } else { this.service.cadastrar(this.veiculoForm.value).subscribe({ next: () => this.router.navigateByUrl('/veiculos'), // error: () => this.onErrorCadastro(), }); } } deletar() { this.service.deletar(this.veiculoForm.value).subscribe({ next: () => this.router.navigateByUrl('/veiculos'), // error: () => this.onErrorDelete(), }); } }
问题原因
- ngModel绑定位置错误:把
[(ngModel)]绑在了<label>标签上,而非checkbox的<input>标签,导致Angular无法正确处理表单控件绑定逻辑,触发writeValue相关错误。 - 属性初始化缺失:如果
Veiculo类中portariaSEJUSP属性无默认值,初始化veiculo对象时该属性为undefined,checkbox绑定布尔值会出现状态异常。 - 表单模式混用:组件声明了响应式表单的
veiculoForm,但实际使用模板驱动表单的ngModel,且veiculoForm从未初始化,造成表单状态混乱。
解决步骤
步骤1:修正checkbox的ngModel绑定
将[(ngModel)]移到<input type="checkbox">上,同时添加name属性(模板驱动表单要求控件有唯一名称):
<div class="form-group form-check"> <input type="checkbox" class="form-check-input" [(ngModel)]="veiculo.portariaSEJUSP" name="portariaSEJUSP"> <label class="form-check-label">Deriva da Portaria SEJUSP/DGPC nº 133/2018</label> </div>
步骤2:确保属性初始化
在Veiculo类中给portariaSEJUSP设置默认布尔值:
export class Veiculo { id?: number; placaOeste?: string; modelo?: string; marca?: string; ano?: string; portariaSEJUSP: boolean = false; // 设置默认值 }
步骤3:统一表单模式(二选一)
选项A:继续使用模板驱动表单
删除未使用的veiculoForm,修改salvar方法直接使用veiculo对象:
export class VeiculoCadastrarEditarComponent implements OnInit { veiculo = new Veiculo(); id: any = 0; constructor( private service: VeiculoService, private router: Router, private activatedRoute: ActivatedRoute ) {} ngOnInit(): void { this.id = this.activatedRoute.snapshot.paramMap.get('id'); if (this.id) { this.service.pesquisarPorId(this.id).subscribe({ next: (data) => (this.veiculo = data), error: (err) => console.log(err), }); } } salvar() { if (this.veiculo && this.veiculo.id) { this.service .atualizar(this.veiculo, this.veiculo.id) .subscribe({ next: () => this.router.navigateByUrl('/veiculos'), // error: () => this.onErrorEdicao(), }); } else { this.service.cadastrar(this.veiculo).subscribe({ next: () => this.router.navigateByUrl('/veiculos'), // error: () => this.onErrorCadastro(), }); } } deletar() { this.service.deletar(this.veiculo).subscribe({ next: () => this.router.navigateByUrl('/veiculos'), // error: () => this.onErrorDelete(), }); } }
选项B:切换为响应式表单(推荐)
初始化veiculoForm并绑定所有控件:
import { FormBuilder, FormGroup } from '@angular/forms'; export class VeiculoCadastrarEditarComponent implements OnInit { veiculoForm!: FormGroup; id: any = 0; constructor( private service: VeiculoService, private router: Router, private activatedRoute: ActivatedRoute, private fb: FormBuilder ) {} ngOnInit(): void { this.veiculoForm = this.fb.group({ id: [{value: '', disabled: true}], placaOeste: [''], modelo: [''], marca: [''], ano: [''], portariaSEJUSP: [false] }); this.id = this.activatedRoute.snapshot.paramMap.get('id'); if (this.id) { this.service.pesquisarPorId(this.id).subscribe({ next: (data) => this.veiculoForm.patchValue(data), error: (err) => console.log(err), }); } } salvar() { if (this.id) { this.service .atualizar(this.veiculoForm.getRawValue(), this.id) .subscribe({ next: () => this.router.navigateByUrl('/veiculos'), // error: () => this.onErrorEdicao(), }); } else { this.service.cadastrar(this.veiculoForm.value).subscribe({ next: () => this.router.navigateByUrl('/veiculos'), // error: () => this.onErrorCadastro(), }); } } deletar() { this.service.deletar(this.veiculoForm.getRawValue()).subscribe({ next: () => this.router.navigateByUrl('/veiculos'), // error: () => this.onErrorDelete(), }); } }
对应HTML修改为响应式绑定:
<div class="card shadow-sm m-5 p-5 bg-light" style="min-width: 60%; max-width: 70%" [formGroup]="veiculoForm"> <mat-toolbar>{{id?'Editar Veiculo':'Cadastrar Veiculo'}}</mat-toolbar> <div class="card-body"> <div class="form-group" *ngIf="id"> <label class="form-label">ID</label> <input type="text" class="form-control" formControlName="id" readonly /> </div> <div class="form-group"> <label class="form-label">Placa Oeste</label> <input type="text" class="form-control" formControlName="placaOeste" /> </div> <div class="form-group"> <label class="form-label">Modelo</label> <input type="text" class="form-control" formControlName="modelo"> </div> <div class="form-group"> <label class="form-label">Marca</label> <input type="text" class="form-control" formControlName="marca"> </div> <div class="form-group"> <label class="form-label">Ano</label> <input type="text" class="form-control" formControlName="ano"> </div> <div class="form-group form-check"> <input type="checkbox" class="form-check-input" formControlName="portariaSEJUSP"> <label class="form-check-label">Deriva da Portaria SEJUSP/DGPC nº 133/2018</label> </div> <div> <button class="btn btn-primary m-2" (click)="salvar()"> Salvar </button> <button type="button" class="btn btn-warning mx-2" routerLink="/veiculos"> Cancelar </button> </div> </div> </div>
验证
完成修改后重新运行项目,编辑场景下checkbox可正常绑定veiculo.portariaSEJUSP属性,不会再抛出writeValue相关错误,业务功能正常。
内容的提问来源于stack exchange,提问作者Victor
相关产品推荐
相关产品推荐

