Angular编辑用户时触发TypeError: Cannot read 'writeValue'空指针错误求助
编辑/更新用户时出现Angular表单错误及样式异常
更新或编辑用户时触发以下错误,同时页面样式显示异常,已排除MgModel相关问题:
in.ts:12 ERROR TypeError: Cannot read properties of null (reading 'writeValue') at setUpControl (forms.mjs:2939:23) at FormGroupDirective.addControl (forms.mjs:4771:9) at FormControlName._setUpControl (forms.mjs:5321:43) at FormControlName.ngOnChanges (forms.mjs:5266:18) at FormControlName.rememberChangeHistoryAndInvokeOnChangesHook (core.mjs:1573:14) at callHook (core.mjs:2497:18) at callHooks (core.mjs:2456:17) at executeInitAndCheckHooks (core.mjs:2407:9) at selectIndexInternal (core.mjs:9100:17) at Module.ɵɵadvance (core.mjs:9083:5)
相关代码
组件TS文件
import { Location } from '@angular/common'; import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; import { User, UsersService } from '@eshop-repository/users'; import { MessageService } from 'primeng/api'; import { timer } from 'rxjs'; @Component({ selector: 'admin-users-form', templateUrl: './users-form.component.html', styles: [] }) export class UsersFormComponent implements OnInit { form!: FormGroup; isSubmitted = false; editmode = false; currentUserId!: string; countries = []; constructor( private messageService: MessageService, private formBuilder: FormBuilder, private usersService: UsersService, private location: Location, private route: ActivatedRoute ) {} ngOnInit(): void { this._initUserForm(); //this._getCountries(); this._checkEditMode(); } private _initUserForm() { this.form = this.formBuilder.group({ name: ['', Validators.required], password: ['', Validators.required], email: ['', [Validators.required, Validators.email]], phone: ['', Validators.required], isAdmin: [false], street: [''], apartment: [''], zip: [''], city: [''], country: [''] }); } // private _getCountries() { // this.countries = this.usersService.getCountries(); // } private _addUser(user: User) { this.usersService.createUser(user).subscribe( (user: User) => { this.messageService.add({ severity: 'success', summary: 'Success', detail: `User ${user.name} is created!` }); timer(2000) .toPromise() .then(() => { this.location.back(); }); }, () => { this.messageService.add({ severity: 'error', summary: 'Error', detail: 'User is not created!' }); } ); } private _updateUser(user: User) { this.usersService.updateUser(user).subscribe( () => { this.messageService.add({ severity: 'success', summary: 'Success', detail: 'User is updated!' }); timer(2000) .toPromise() .then(() => { this.location.back(); }); }, () => { this.messageService.add({ severity: 'error', summary: 'Error', detail: 'User is not updated!' }); } ); } private _checkEditMode() { this.route.params.subscribe((params) => { if (params['id']) { this.editmode = true; this.currentUserId = params['id']; this.usersService.getUser(params['id']).subscribe((user) => { this.userForm['name'].setValue(user.name); this.userForm['email'].setValue(user.email); this.userForm['phone'].setValue(user.phone); this.userForm['isAdmin'].setValue(user.isAdmin); this.userForm['street'].setValue(user.street); this.userForm['apartment'].setValue(user.apartment); this.userForm['zip'].setValue(user.zip); this.userForm['city'].setValue(user.city); this.userForm['country'].setValue(user.country); this.userForm['password'].setValidators([]); this.userForm['password'].updateValueAndValidity(); }); } }); } onSubmit() { this.isSubmitted = true; if (this.form.invalid) { return; } const user: User = { id: this.currentUserId, name: this.userForm['name'].value, email: this.userForm['email'].value, password: this.userForm['password'].value, phone: this.userForm['phone'].value, isAdmin: this.userForm['isAdmin'].value, street: this.userForm['street'].value, apartment: this.userForm['apartment'].value, zip: this.userForm['zip'].value, city: this.userForm['city'].value, country: this.userForm['country'].value }; if (this.editmode) { this._updateUser(user); } else { this._addUser(user); } } onCancle() { this.location.back(); } get userForm() { return this.form.controls; } }
HTML文件
<p-toast></p-toast> <div class="admin-page"> <p-card [header]="editmode ? 'Edit User' : 'Add User'" subheader="You can add or edit users here"> <div class="p-grid p-mb-5"> <div class="p-col-12"> <p-toolbar> <div class="p-toolbar-group-left"></div> <div class="p-toolbar-group-right"> <p-button styleClass="p-button-primary p-mr-2" [label]="editmode ? 'Update' : 'Create'" icon="pi pi-plus" (click)="onSubmit()" ></p-button> <p-button styleClass="p-button-secondary" label="Cancle" icon="pi pi-arrow-circle-left" (click)="onCancle()" ></p-button> </div> </p-toolbar> </div> </div> <div class="p-grid"> <div class="p-col-12"> <form [formGroup]="form"> <div class="p-fluid p-formgrid p-grid"> <div class="p-field p-col-4"> <label for="name">Name</label> <input formControlName="name" id="name" type="text" pInputText /> <small *ngIf="userForm['name'].invalid && isSubmitted" class="p-error" >Name is required</small > </div> <div class="p-field p-col-4"> <label for="email">Email</label> <input formControlName="email" id="email" type="text" pInputText /> <small *ngIf="userForm['email'].invalid && isSubmitted" class="p-error" > <span>*ngIf="userForm['email'].invalid && isSubmitted">email is required</span> <span *ngIf="userForm['email'].invalid && isSubmitted">email is invalid</span> > </small> </div> <div class="p-field p-col-4"> <label for="password">Password</label> <input formControlName="password" id="password" type="password" pInputText /> <small *ngIf="userForm['password'].invalid && isSubmitted" class="p-error" >Password is required</small > </div> <div class="p-field p-col-4"> <label for="color">Phone</label><br /> <p-inputMask mask="(999) 999-9999" formControlName="phone" placeholder="(999) 999-9999" ></p-inputMask> <small *ngIf="userForm['phone'].invalid && isSubmitted" class="p-error" >phone is required</small > </div> <div class="p-field p-col-4"> <label for="isAdmin">Is Admin</label><br /> <p-inputSwitch formControlName="isAdmin"></p-inputSwitch> </div> </div> <div class="p-fluid p-formgrid p-grid"> <div class="p-field p-col-4"> <label for="street">Street</label> <input formControlName="street" id="street" type="text" pInputText /> </div> <div class="p-field p-col-4"> <label for="street">Apartment</label> <input formControlName="apartment" id="apartment" type="text" pInputText /> </div> <div class="p-field p-col-4"> <label for="street">Zip Code</label> <input formControlName="zip" id="zip" type="text" pInputText /> </div> <div class="p-field p-col-4"> <label for="city">City</label> <input formControlName="city" id="city" type="text" pInputText /> </div> <div class="p-field p-col-4"> <label for="country">Country</label><br /> <p-dropdown [options]="countries" formControlName="country" optionLabel="name" optionValue="id" [filter]="true" filterBy="name" [showClear]="true" placeholder="Select a Country" ></p-dropdown> </div> </div> </form> </div> </div> </p-card> </div>
服务TS文件
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { User } from '../models/user'; import { environment } from 'environments/environment'; @Injectable({ providedIn: 'root' }) export class UsersService { apiURLUsers = environment.apiUrl + 'users'; constructor(private http: HttpClient) { // countriesLib.registerLocale(require('i18n-iso-countries/langs/en.json')); } getUsers(): Observable<User[]> { return this.http.get<User[]>(this.apiURLUsers); } getUser(userId: string): Observable<User> { return this.http.get<User>(`${this.apiURLUsers}/${userId}`); } createUser(user: User): Observable<User> { return this.http.post<User>(this.apiURLUsers, user); } updateUser(user: User): Observable<User> { return this.http.put<User>(`${this.apiURLUsers}/${user.id}`, user); } deleteUser(userId: string): Observable<any> { return this.http.delete<any>(`${this.apiURLUsers}/${userId}`); } getUsersCount(): Observable<number> { return this.http .get<number>(`${this.apiURLUsers}/get/count`) .pipe(map((objectValue: any) => objectValue.userCount)); } }
问题排查与修复方案
1. 表单控件绑定异常修复
该错误源于表单控件无法正常关联FormControl实例,结合代码需做以下调整:
- 恢复
_getCountries方法的调用,确保countries数组获取到有效数据,避免p-dropdown组件初始化失败:ngOnInit(): void { this._initUserForm(); this._getCountries(); this._checkEditMode(); } private _getCountries() { this.countries = this.usersService.getCountries(); } - 修正HTML中邮箱错误提示的语法错误,将字符串形式的
*ngIf改为Angular指令:<small *ngIf="userForm['email'].invalid && isSubmitted" class="p-error"> <span *ngIf="userForm['email'].errors?.['required']">email is required</span> <span *ngIf="userForm['email'].errors?.['email']">email is invalid</span> </small> - 使用
patchValue批量赋值表单字段,替代逐个setValue,提升代码稳定性:this.form.patchValue({ name: user.name, email: user.email, phone: user.phone, isAdmin: user.isAdmin, street: user.street, apartment: user.apartment, zip: user.zip, city: user.city, country: user.country });
2. 样式异常修复
样式异常通常是PrimeNG模块未正确导入导致,需在当前组件所在模块中导入所需模块:
import { CardModule } from 'primeng/card'; import { ToolbarModule } from 'primeng/toolbar'; import { ButtonModule } from 'primeng/button'; import { InputTextModule } from 'primeng/inputtext'; import { InputMaskModule } from 'primeng/inputmask'; import { InputSwitchModule } from 'primeng/inputswitch'; import { DropdownModule } from 'primeng/dropdown'; import { ToastModule } from 'primeng/toast'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; @NgModule({ imports: [ FormsModule, ReactiveFormsModule, CardModule, ToolbarModule, ButtonModule, InputTextModule, InputMaskModule, InputSwitchModule, DropdownModule, ToastModule ] }) export class AdminModule { }
内容的提问来源于stack exchange,提问作者ENN9
相关产品推荐
相关产品推荐

