在Angular中实现ControlValueAccessor时,为什么需要在writeValue中调用onChange和onTouch?

时间:2017-10-14 09:56:56

标签: angular typescript custom-component

我已经实现了以下组件。它的工作和行为符合预期。然而,由于ControlValueAccessor的实现对我来说是新的,我不得不follow a blog而不了解几个部分的更深层细节。所以这是一种“它有效,但为什么?!”的情况。

@Component({ selector: ..., templateUrl: ..., styleUrls: ...,
  providers: [{ provide: NG_VALUE_ACCESSOR, 
                useExisting: forwardRef(() => InputTextComponent), 
                multi: true }]
})
export class InputComponent implements ControlValueAccessor {

  constructor() { }
  @Input() info: string;
  onChange: any = () => { }
  onTouch: any = () => { }

  writeValue(input: any): void {
    this.info.value = input;
    // this.onChange(input);
    // this.onTouch();
  }

  registerOnChange(_: any): void { this.onChange = _; }
  registerOnTouched(_: any): void { this.onTouch = _; }

  setDisabledState?(state: boolean): void { }

  onEdit(value: string): void { this.onChange(value); }
}

当我开始工作时,我评论了writeValue(...)方法的第二和第三行,据我所知,没有任何事情发生。这些调用也始终由other blogs提出,因此我确信省略它们是不恰当的。但是,我不相信魔法,宁愿有一个具体的理由做事。

为什么在onChange(...)中执行对onTouch(...)writeValue(...)的调用很重要?会出现什么问题以及在什么情况下可以预期?

作为一项副作用,我还试图评论the other methods并发现当我移除setDisabledState(...)时我无法告诉任何香蕉。什么时候可以预期会导致问题?是否真的需要实现(我在括号前后看到带有问号的版本,参数如此:setDisabledState?(state: boolean): void { },但也像这样:setDisabledState(state: boolean)?: void { })。

2 个答案:

答案 0 :(得分:8)

阅读这篇文章,详细解释了ControlValueAccessor

如果组件应该用作Angular表单的一部分,通常需要在组件上实现ControlValueAcceessor接口。

  

我注释掉了writeValue(...)方法的第二行和第三行   而且,据我所知,没有任何事情发生。

这可能是因为您没有应用将formControl链接到自定义输入组件的任何表单指令 - ngModelFormControlFormControl使用writeValue InputComponent方法进行通信。

以下是我在上面引用的文章中的图片:

enter image description here

writeValue formControl方法用于将值设置为本机表单控件。 registerOnChange使用formControl方法来注册每次更新本机表单控件时都会触发的回调。 registerOnTouched方法用于指示用户与控件进行交互。

  

为什么执行对onChange(...)的调用很重要   writeValue(...)中的onTouch(...)?什么会出错,在什么地方   情况可以预期吗?

这是实现ControlValueAcceessor的自定义控件通知Angular FormControl输入中的值已更改或用户与控件交互的机制。

  

...发现我不能告诉任何香蕉   删除了setDisabledState(...)......是否真的需要实现?

如界面中所指定,当控件状态更改为“DISABLED”或从“DISABLED”更改时,此函数将由表单API调用。根据值,它应启用或禁用适当的DOM元素。 如果您希望在关联的FormControl的状态变为disabled时收到通知,然后您可以执行某些自定义逻辑(例如,禁用您的输入组件),则需要实现它。

答案 1 :(得分:1)

我不认为被接受的答案能以简洁的方式回答最核心的问题:

为什么在writeValue(...)中执行对onChange(...)和onTouch(...)的调用很重要?

不是。您不需要需要在writeValue中调用onChange。这不是预期的用法(请参见下面的文档链接)。

出什么问题了,在什么情况下可以预见?

没什么可出错的。详细的答案:

如果您onChange内部呼叫writeValue,您会注意到:

  • 第一次onChange调用不执行任何操作。这是因为在首次调用writeValue时,尚未注册onChange回调(即未调用registerOnChange)。
  • 以后从writeValue内部调用onChange会更新模型(使用emitModelToViewChange = false来避免递归调用writeValue),它会使用您从模型中获得的值(刚收到的值)进行更新。

换句话说,除非您依靠您的组件以某种方式立即更改其在writeValue中接收的值,然后将这些更改传递给您的模型,但仅在 second / em>和更高版本的writeValue调用,可以安全地不从writeValue调用onChange。

在此处查看文档示例:https://angular.io/api/forms/ControlValueAccessor