You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 12:28:08