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

Angular自定义表单验证器报错:Cannot read properties of undefined (reading 'found')

Angular自定义表单验证器修复与实现

问题

提交表单调用API后,需根据返回结果设置notExisted为true时显示“Not found”错误提示,但提交后控制台抛出TypeError: Cannot read properties of undefined (reading 'found')(编译后变量名映射导致显示为found,实际为读取notExisted相关属性失败)。

报错信息

ERROR Error: Uncaught (in promise): TypeError: Cannot read properties of undefined (reading 'found')
TypeError: Cannot read properties of undefined (reading 'found')
at src_app_main_pages_pages_module_ts.js:906:27
at new ZoneAwarePromise (polyfills.js:10389:21)
at push.9650.AuthForgotPasswordV2Component.existValidator (src_app_main_pages_pages_module_ts.js:904:16)
at vendor.js:76993:12
at Array.map (<anonymous>)
at executeValidators (vendor.js:76992:21)
at FormControl._composedValidatorFn (vendor.js:77023:24)
at FormControl._runValidator (vendor.js:78668:36)
at FormControl.updateValueAndValidity (vendor.js:78631:28)
at FormControl.setValue (vendor.js:79115:12)
at resolvePromise (polyfills.js:10309:19)
at new ZoneAwarePromise (polyfills.js:10391:9)
at push.9650.AuthForgotPasswordV2Component.existValidator (src_app_main_pages_pages_module_ts.js:904:16)
at vendor.js:76993:12
at Array.map (<anonymous>)
at executeValidators (vendor.js:76992:21)
at FormControl._composedValidatorFn (vendor.js:77023:24)
at FormControl._runValidator (vendor.js:78668:36)
at FormControl.updateValueAndValidity (vendor.js:78631:28)
at FormControl.setValue (vendor.js:79115:12)

现有代码

组件代码

public 
public notExisted = true;
get f() {
  return this.forgotPasswordForm.controls;
}
//API 
onSubmit() {
  this.submitted = true;
  // stop here if form is invalid
  if (this.forgotPasswordForm.invalid) {
    return;
  }

  this._authenticationService.forgotPassword(this.f.email.value)
    .subscribe(
      (data) => {
        console.log(data.statusCode)
        if (data.statusCode == 200 || data.statusCode == '200') {
          this.notExisted = false;
          this.sended = true;
        } else if (data.statusCode == 204 || data.statusCode == '204') {
          this.notExisted = true;
          this.sended = false;
          this.error = data.content;;
        } else {
          this.notExisted = false;
          this.sended = false
          this.error = data.content;
        }
      }
}
//custom validator
existValidator(control: FormControl) {
  return new Promise((resolve, reject) => {
    if (this.notExisted !== undefined) {
      if (this.notExisted == true) {
        resolve({ notExisted: true });
      } else {
        resolve(null);
      }
    } else {
      resolve(null);
    }
  });
}

ngOnInit(): void {
  console.log(this.currentDate);
  this.forgotPasswordForm = this._formBuilder.group({
    email: ['', [Validators.required, Validators.email,this.existValidator]]
  });

  // Subscribe to config changes
  this._coreConfigService.config.pipe(takeUntil(this._unsubscribeAll)).subscribe(config => {
    this.coreConfig = config;
  });
}

HTML代码

<form class="auth-forgot-password-form mt-2" [formGroup]="forgotPasswordForm" (ngSubmit)="onSubmit()">
  <div class="form-group">
    <label class="form-label" for="forgot-password-email">Email</label>
    <input class="form-control" type="text" formControlName="email" placeholder="john@example.com"
      aria-describedby="forgot-password-email" autofocus="" tabindex="1"
      [ngClass]="{ 'is-invalid': submitted && f.email.errors}" />
    <div *ngIf="submitted && f.email.errors" class="invalid-feedback">
      <div *ngIf="f.email.errors.required">Email is required</div>
      <div *ngIf="f.email.errors.email">Email must be a valid email address</div>
      <div *ngIf="f.email.errors.notExisted">Not found</div>
    </div>
  </div>
  <button class="btn btn-primary btn-block" tabindex="2" rippleEffect>Send reset link</button>
</form>

修复方案

核心问题分析

  1. this指向丢失:验证器函数作为参数传递给FormControl时,函数内部的this不再指向组件实例,导致无法读取this.notExisted。
  2. 验证时机错误:原验证器会在表单值变化时自动执行,初始notExisted=true会导致页面加载时就显示错误,不符合“提交后根据API结果显示”的需求。

修改后的完整代码

组件代码

public notExisted = false; // 初始设为false,避免未提交就显示错误
public submitted = false;
public sended = false;
public error = '';
get f() {
  return this.forgotPasswordForm.controls;
}

// API提交逻辑
onSubmit() {
  this.submitted = true;
  this.error = '';
  
  if (this.forgotPasswordForm.invalid) {
    return;
  }

  this._authenticationService.forgotPassword(this.f.email.value)
    .subscribe(
      (data) => {
        console.log(data.statusCode);
        if (data.statusCode == 200 || data.statusCode == '200') {
          this.notExisted = false;
          this.sended = true;
        } else if (data.statusCode == 204 || data.statusCode == '204') {
          this.notExisted = true;
          this.sended = false;
          this.error = data.content;
        } else {
          this.notExisted = false;
          this.sended = false;
          this.error = data.content;
        }
        // API返回后手动触发表单验证,更新错误状态
        this.f.email.updateValueAndValidity({ emitEvent: false });
      },
      (err) => {
        // 处理API请求失败情况
        this.notExisted = false;
        this.sended = false;
        this.error = 'Request failed';
        this.f.email.updateValueAndValidity({ emitEvent: false });
      }
    );
}

// 自定义验证器:改为箭头函数,保留this指向
existValidator = (control: FormControl) => {
  return new Promise((resolve) => {
    // 仅在表单提交后,根据notExisted判断是否返回错误
    if (this.submitted && this.notExisted) {
      resolve({ notExisted: true });
    } else {
      resolve(null);
    }
  });
}

ngOnInit(): void {
  console.log(this.currentDate);
  this.forgotPasswordForm = this._formBuilder.group({
    email: ['', [Validators.required, Validators.email, this.existValidator]]
  });

  this._coreConfigService.config.pipe(takeUntil(this._unsubscribeAll)).subscribe(config => {
    this.coreConfig = config;
  });
}

HTML代码(无需修改)

<form class="auth-forgot-password-form mt-2" [formGroup]="forgotPasswordForm" (ngSubmit)="onSubmit()">
  <div class="form-group">
    <label class="form-label" for="forgot-password-email">Email</label>
    <input class="form-control" type="text" formControlName="email" placeholder="john@example.com"
      aria-describedby="forgot-password-email" autofocus="" tabindex="1"
      [ngClass]="{ 'is-invalid': submitted && f.email.errors}" />
    <div *ngIf="submitted && f.email.errors" class="invalid-feedback">
      <div *ngIf="f.email.errors.required">Email is required</div>
      <div *ngIf="f.email.errors.email">Email must be a valid email address</div>
      <div *ngIf="f.email.errors.notExisted">Not found</div>
    </div>
  </div>
  <button class="btn btn-primary btn-block" tabindex="2" rippleEffect>Send reset link</button>
</form>

关键修改说明

  1. this指向修复:将existValidator改为箭头函数,确保函数内部的this始终指向组件实例,解决Cannot read properties of undefined错误。
  2. 初始值调整:将notExisted初始值设为false,避免页面加载时就显示“Not found”错误。
  3. 手动触发验证:在API请求完成后调用updateValueAndValidity({ emitEvent: false }),触发验证器更新表单错误状态,同时emitEvent: false避免触发值变化的循环事件。
  4. 验证逻辑优化:验证器中增加this.submitted判断,仅在表单提交后才根据notExisted返回错误,符合业务需求。

内容的提问来源于stack exchange,提问作者LHP1999

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 07:32:33