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

Why do I need call onChange and onTouch in writeValue when implementing ControlValueAccessor in Angular?

我已经实现了以下组件。它按预期工作和运行。然而,由于 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 也一直建议这些调用,因此我确信省略它们是不合适的。但是,我不相信魔法,更喜欢做事有具体的理由。

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

作为支线任务,我还尝试注释掉 the other methods,结果发现当我删除 setDisabledState(...) 时我无法说出任何事情。什么时候可以预期那个会引起问题?它真的需要实现吗(我见过括号前后都有问号的版本,参数如下:setDisabledState?(state: boolean): void { } 但也像这样:setDisabledState(state: boolean)?: void { })。

阅读这篇详细解释 ControlValueAccessor 的文章:

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

I commented out the second and third line of writeValue(...) method and, as far I can tell, nothing broke.

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

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

writeValue 方法被 formControl 用来设置本机表单控件的值。 registerOnChange 方法被 formControl 用来注册一个预期在每次更新本机表单控件时触发的回调。 registerOnTouched 方法用于指示用户与控件交互。

Why is it important to execute the call to onChange(...) and onTouch(...) in writeValue(...)? What will go wrong and under what circumstances can it be expected?

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

...discovered that I couldn't tell anything going bananas when I removed setDisabledState(...)...Does it really need to be implemented?

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

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

Why is it important to execute the call to onChange(...) and onTouch(...) in writeValue(...)?

不是。您 不需要 需要在 writeValue 中调用 onChange。这不是预期用途(请参阅下面的文档 link)。

What will go wrong and under what circumstances can it be expected?

预计不会出错。详细回答:

如果您从内部writeValue调用onChange,您会注意到:

  • 第一个 onChange 调用什么都不做。那是因为第一次调用writeValue时,onChange回调还没有被注册(即registerOnChange没有被调用)。
  • 稍后从 writeValue 内部调用 onChange 会使用您从模型中给它的值(您刚刚收到的值)更新您的模型(使用 emitModelToViewChange = false 以避免递归调用 writeValue)。

换句话说,除非您依靠您的组件以某种方式立即更改它在 writeValue 中接收到的值,并将这些更改传递回您的模型,但 second 和以后的 writeValue 调用,您可以安全地不从 writeValue 调用 onChange。

请参阅此处文档中的示例:https://angular.io/api/forms/ControlValueAccessor