【问题标题】:Angular - controlValueAccessor method writeValue() not being calledAngular - controlValueAccessor 方法 writeValue() 未被调用
【发布时间】:2019-01-05 00:47:49
【问题描述】:

Angular 版本:6.1

我正在尝试使用 Kara Erickson 所称的 Composite ControlValueAccessor 来实现自定义控件,如演示文稿Angular Forms – Kara Erickson – AngularConnect 2017 中所示。控件的值应设置为两个子输入值之一。

问题是writeValue() 似乎只在最初被调用,而不是在进一步的值更改时被调用。

这是stackblitz demo

form.html

<form #f="ngForm" (ngSubmit)="f.form.valid && Submit(f)">
    <confirm-password name='password' ngModel #pwd='ngModel'></confirm-password>
    <div>{{pwd.status}}</div>
</form>

确认密码.html

<div [formGroup]='pass'>
  <label>Password:
    <input type="password" formControlName='pwd1' (blur)='onTouched()'>
  </label>
  <div *ngIf="controlDir.touched && controlDir.control.errors?.required" 
         class="error">Password is required.</div>

  <label>Please re-enter password:
    <input type="password" formControlName='pwd2' (blur)='onTouched()'>
  </label>
  <div class='error' *ngIf='controlDir.touched && controlDir.control.errors?.notEqual && 
                            !controlDir.errors?.required'>Passwords do not match.</div>
</div>

确认密码.ts

import { Component, Self, OnInit, OnDestroy } from '@angular/core';
import { ControlValueAccessor, AbstractControl, NgControl,
         ValidatorFn, Validators, FormGroup, FormControl } from '@angular/forms';
import { Subscription } from 'rxjs';

@Component({
  selector: 'confirm-password',
  templateUrl: './confirm-password.component.html',
  styleUrls: ['./confirm-password.component.css']
})
export class ConfirmPasswordComponent implements ControlValueAccessor, OnInit, OnDestroy
{
  password: string; // the value of the control should be set to this value
  pass = new FormGroup({
    pwd1: new FormControl(),
    pwd2: new FormControl()
  });
  private onChange: (value: string) => void;
  private onTouched: (value: string) => void;
  private valueChanges: Subscription;

  constructor(@Self() public controlDir: NgControl)
  {
    controlDir.valueAccessor = this;
  }

  ngOnInit()
  {
    const control = this.controlDir.control;

    let myValidators = [
            Validators.required, 
            equalValidator(this.pass.controls.pwd1,
                           this.pass.controls.pwd2)
        ]
    // ovoid overwriting any existing validators
    let validators = control.validator
      ? [control.validator, ...myValidators]
      : [...myValidators];
    control.setValidators(validators);
    control.updateValueAndValidity();
  }

  writeValue(val: any)
  {
    /* whether everything inside of this method is commented out or not 
       doesn't seem to affect anything */
    console.log(val);
    //val && this.pass.setValue(val, {emitEvent: false});
    this.password = val;
  }

  registerOnChange(fn: (val: any) => void)
  {
    this.valueChanges = this.pass.valueChanges.subscribe(fn);
  }

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

  setDisabledState(disabled: boolean)
  {
    disabled ? this.pass.disable() : this.pass.enable();
  }

  ngOnDestroy()
  {
    this.valueChanges.unsubscribe();
  }

}

export function equalValidator(el1, el2): ValidatorFn
{
  return (ctrl: AbstractControl): {[key: string]: any} | null => {
    const notEqual = el1.value !== el2.value;

    return notEqual ?  {"notEqual": true} : null;
  };
}

【问题讨论】:

  • 链接到 stackblitz 编辑器 plz
  • 哎呀,没有意识到我链接到了错误的链接。现在应该可以工作了。
  • 对我来说很好:stackblitz.com/edit/angular-ukowfl?file=src/app/… - 按下按钮
  • 你改了吗?它对我来说根本不起作用,控制台中有几个错误..

标签: angular


【解决方案1】:

writeValue 的目的是通知你的组件外部的变化/父组件的表单控件。您通常想做的是将外部的更改绑定到局部变量。

为了通知外界您的组件内部的变化,您需要调用onChange 方法。您可以调用它,就像您通常调用事件发射器一样。

我在这里整理了几个例子:https://www.tsmean.com/articles/angular/angular-control-value-accessor-example/

【讨论】:

  • 在阅读了十几篇文章和教程之后,您的文章和教程最终变得有意义。谢谢!关键部分是,“您自己不提供 onTouch 和 onChange 函数的实现。它们将由 Angular 提供。......您可以在何时调用这些函数时发表意见。”。跨度>
【解决方案2】:

writeValue 在您更改控件值(例如通过设置支持 ngModel 属性或控件值)而不是通过用户输入时调用。

https://stackblitz.com/edit/angular-hu2bfs?file=src/app/app.module.ts

我已经删除了由误用ngModel引起的错误

如您所见,如果您输入额外的输入新值并按下按钮,则会调用writeValue

总结一下——writeValue 在控制器更改模型值时被调用,writeValue 的目的是做必要的事情以通过自定义控件反映模型更改。

【讨论】:

  • 谢谢,我明白了。虽然您回答了这个问题,但我希望我会改写它,而不是假设 writeValue 会解决我的问题,因为它实际上并不能帮助我实现将 confirm-password 值设置为子控件值之一的主要目标,而不是包含两者的对象。不过,谢谢。
  • 但是如果FormControl 是从父级传下来的,我该如何访问它?当然,从智能/愚蠢组件的角度传递它比在其内部创建它更有意义。您还在 stackblitz 中混合了响应式 + 模板表单。你不应该使用 ngModel 和 FormControl。
猜你喜欢
  • 2020-01-08
  • 2017-08-26
  • 2018-03-26
  • 1970-01-01
  • 1970-01-01
  • 2018-06-29
  • 2017-10-23
  • 2014-03-04
  • 2020-02-25
相关资源
最近更新 更多