Angular 12打包后报TypeError: 无法读取null的writeValue属性如何解决?
报错原因
这个报错是Angular开发模式和生产构建的初始化执行顺序差异导致的:
- 开发模式为JIT编译,初始化流程有额外校验逻辑,NgControl实例的
control属性会在组件渲染前完成初始化,所以不会触发空指针问题 - 生产构建默认启用AOT编译和代码优化,组件渲染时
NgControl.control可能还未完成赋值,直接访问其属性/方法就会触发空值报错,你遇到的writeValue读null就是因为ngControl或者其绑定的control实例未初始化完成就被调用了
解决方案
第一步:修改component.ts代码
调整valueAccessor赋值时机,添加统一的空值防护,补全ControlValueAccessor的方法实现:
import { Component, Input, Self, OnInit } from '@angular/core'; import {AbstractControl, ControlValueAccessor, NgControl} from '@angular/forms'; @Component({ selector: 'app-text-input', templateUrl: './text-input.component.html', styleUrls: ['./text-input.component.css'] }) export class TextInputComponent implements ControlValueAccessor, OnInit { @Input() label: string; @Input() type = 'text'; // 统一获取control实例,内置空值判断 get control(): AbstractControl | null { return this.ngControl?.control; } constructor(@Self() public ngControl: NgControl) {} ngOnInit(): void { // 把valueAccessor赋值移到ngOnInit,确保NgControl已完成初始化 if (this.ngControl) { this.ngControl.valueAccessor = this; } } checkRequired(control: AbstractControl | null): boolean { if (!control?.validator) return false; const validator = control.validator({} as AbstractControl); return !!validator?.required; } writeValue(obj: any): void { this.control?.setValue(obj, { emitEvent: false }); } registerOnChange(fn: any): void { this.control?.registerOnChange(fn); } registerOnTouched(fn: any): void { this.control?.registerOnTouched(fn); } setDisabledState?(isDisabled: boolean): void { this.control?.[isDisabled ? 'disable' : 'enable'](); } }
第二步:修改HTML模板
把所有直接访问ngControl.control的地方替换为上面封装的control属性,补充空值可选链防护:
<div class="p-field"> <label>{{label}} <span class="p-error">{{checkRequired(control) ? '*' : ''}}</span></label> <input class="full-width" [type]="type" pInputText [formControl]="control"> <small *ngIf="control?.touched && control?.errors?.required" class="p-error">{{label}}为必填项</small> <small *ngIf="control?.errors?.minlength" class="p-error"> {{label}}长度最少为{{control?.errors.minlength['requiredLength']}}位 </small> <small *ngIf="control?.errors?.maxlength" class="p-error"> {{label}}长度最多为{{control?.errors.maxlength['requiredLength']}}位 </small> <small *ngIf="control?.errors?.email" class="p-error"> 该邮箱地址无效 </small> <small *ngIf="control?.errors?.isMatching" class="p-error">两次输入的密码不匹配</small> </div>
修改后重新打包即可解决生产环境报错问题,同时开发模式的功能也不会受影响。
内容的提问来源于stack exchange,提问作者Islam Mohamed
相关产品推荐
相关产品推荐

