Angular自定义密码组件报错:No value accessor for form control 'password'
解决Angular自定义密码组件的「No value accessor for form control」错误
错误原因
Angular的formControlName指令要求绑定的组件必须实现ControlValueAccessor接口——这是Angular表单系统与自定义组件之间的桥梁,负责在表单控件和组件之间同步值、传递变更通知。你的自定义密码组件没有实现该接口,所以表单系统无法找到对应的值访问器,触发报错。
修复步骤
1. 修改密码组件类(password.component.ts)
实现ControlValueAccessor接口,并提供NG_VALUE_ACCESSOR令牌,让Angular识别这个组件作为表单控件的载体:
import { Component, Input, OnInit, forwardRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; @Component({ selector: 'app-password-field', templateUrl: './password-field.component.html', styleUrls: ['./password-field.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => PasswordFieldComponent), multi: true } ] }) export class PasswordFieldComponent implements OnInit, ControlValueAccessor { isPasswordHidden = true; @Input() titleOfField!: string; @Input() hintLabel!: string; @Input() name!: string; // 存储当前输入值 private innerValue: string = ''; // 变更回调函数 private onChange: (value: string) => void = () => {}; // 触摸回调函数 private onTouched: () => void = () => {}; constructor() {} ngOnInit(): void {} togglePassVisibility(e: Event) { e.preventDefault(); this.isPasswordHidden = !this.isPasswordHidden; } // ControlValueAccessor接口方法:设置组件值 writeValue(value: string): void { this.innerValue = value || ''; } // ControlValueAccessor接口方法:注册变更回调 registerOnChange(fn: (value: string) => void): void { this.onChange = fn; } // ControlValueAccessor接口方法:注册触摸回调 registerOnTouched(fn: () => void): void { this.onTouched = fn; } // 处理输入框值变化,通知表单系统 onInputChange(value: string): void { this.innerValue = value; this.onChange(value); } // 处理输入框触摸事件,通知表单系统 onInputBlur(): void { this.onTouched(); } }
2. 修改密码组件模板(password.component.html)
移除内部input的[formControlName]绑定,改为绑定值和事件,同步到组件的innerValue并通知表单:
<mat-form-field appearance="fill" hintLabel="{{ hintLabel }}"> <mat-label>{{ titleOfField }}</mat-label> <input matInput [type]="isPasswordHidden ? 'password' : 'text'" [name]="name" [value]="innerValue" (input)="onInputChange($event.target?.value)" (blur)="onInputBlur()" /> <button mat-icon-button matSuffix (click)="togglePassVisibility($event)" [attr.aria-label]="'Hide password'" [attr.aria-pressed]="isPasswordHidden" class="change-visibility" > <mat-icon>{{ isPasswordHidden ? "visibility_off" : "visibility" }}</mat-icon> </button> </mat-form-field>
3. 修改父组件的组件使用
移除formControlName输入属性,直接在自定义组件上绑定formControlName即可:
<app-wrapper> <form class="container" (submit)="userLoginClick()" [formGroup]="form"> <app-password-field titleOfField="Enter your password" hintLabel="Use at least 6 symbols" name="password" formControlName="password" ></app-password-field> </form> </app-wrapper>
关键说明
ControlValueAccessor的三个核心方法:writeValue(表单向组件传值)、registerOnChange(组件向表单通知值变更)、registerOnTouched(组件通知表单已被触摸)。- 通过
providers数组注册NG_VALUE_ACCESSOR,让Angular把这个组件当作表单控件的有效值访问器。 - 内部input的事件处理确保组件值和表单控件值双向同步。
内容的提问来源于stack exchange,提问作者Vova Aleshchenko
相关产品推荐
相关产品推荐

