访问自定义表单控件的有效值

15

我创建了一个自定义组件,代表密码表单控件(以下代码简化)。

PasswordComponent(html)

<form [formGroup]="passwordForm">
  ...
  <input formControlName="password" type="password">
</form>

PasswordComponent (ts)

密码组件(ts)
...
@Component({
  selector: 'password',
  templateUrl: './password.component.html',
  styleUrls: ['./password.component.css'],
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => PasswordComponent),
    multi: true
  }]
})
export class PasswordComponent implements ControlValueAccessor {

  passwordForm: FormGroup;
  onChange = (password: string) => { };  
  onTouched = () => { };                  

  constructor() {
    this.passwordForm = new FormGroup({
      ...
      password: new FormControl('')
    });

    this.passwordForm.valueChanges.subscribe(data => this.onChange(this.value));
  }

  get value(): string {
    return this.passwordForm.get('password').value;
  }

  writeValue(password: string): void {
    this.passwordForm.get('password').setValue(password);
    this.onChange(this.value);
  }

  registerOnChange(fn: any): void { this.onChange = fn; } 

  registerOnTouched(fn: any): void { this.onTouched = fn; }

  setDisabledState?(isDisabled: boolean): void { }
}

我在其他组件中使用它,而不是标准的输入元素:

<form [formGroup]="userForm">
  ...
  <password formControlName="password"></password>
</form>

验证器来自外部表单(它们没有在PasswordComponent内定义)

this.userForm = fb.group({
  ...
  password: ['', [Validators.minLength(10), Validators.maxLength(100)]] 
});

我的问题是:如何从PasswordComponent内部获取<password>元素的有效性?我想根据有效性样式化它。换句话说,我如何从代表此控件的PasswordComponent中获取用户表单中“密码”控件的有效性。

2个回答

21

因为我们会出现循环依赖错误,所以无法直接从DI系统中获取NgControl实例。以下图表显示了如果我们在自定义值访问器中注入NgControl会发生什么:

enter image description here

现在应该清楚了,我们有一个NgControl -> FormControlName -> ValueAccessor -> CustomValueAccessor -> NgControl的循环依赖

为了解决这个问题,你可以利用Injector来实现:

component.ts

import { NgControl } from '@angular/forms';
export class PasswordComponent implements ControlValueAccessor {
  ...
  ngControl: NgControl;

  constructor(private inj: Injector) {
    ...
  }

  ngOnInit() {
    this.ngControl = this.inj.get(NgControl)
  }

模板.html

{{ ngControl.control.valid }}

Plunker示例


1
这个功能非常好用,但是它看起来很丑,难道只有我这么认为吗?它看起来像一个非常基本的使用情况。 - dendimiiii
1
你能详细解释一下这里发生了什么吗?为什么会出现循环依赖错误?为什么注入器没有这个问题? - Ced
感谢您提供的视觉展示,这些内容对 Angular 文档的编写会有所裨益! - Jessy
当我执行 this.inj.get(NgControl) 时,我得到一个 FormControlName,但它是无用的,因为 formControlname.parent.control 是一个 formgroup,而且我不知道哪个控件是“自定义控件”。 - Andre Elrico
1
我在错误的生命周期中。在 ngAfterViewInit 中,我可以读取 name 然后获取正确的控件。 _parent 是私有的。 - Andre Elrico
谢谢。这对我帮助很大。我已经浪费了两天时间来覆盖表单控件中的自定义值获取器。 - Tomislav Brabec

3

解决这个问题的另一种方法是从提供程序中删除NG_VALUE_ACCESSOR,只需注入NgControl即可。使用NgControl实例,该组件将被注册为ValueAccessor。

constructor(
..., 
@Optional() @Self() public ngControl: NgControl,
...,
) {
// Setting the value accessor directly (instead of using
// the providers) to avoid running into a circular import.
if (this.ngControl != null) { this.ngControl.valueAccessor = this; }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接