为啥在 Angular 中实现 ControlValueAccessor 时需要在 writeValue 中调用 onChange 和 onTouch?
Posted
技术标签:
【中文标题】为啥在 Angular 中实现 ControlValueAccessor 时需要在 writeValue 中调用 onChange 和 onTouch?【英文标题】:Why do I need call onChange and onTouch in writeValue when implementing ControlValueAccessor in Angular?为什么在 Angular 中实现 ControlValueAccessor 时需要在 writeValue 中调用 onChange 和 onTouch? 【发布时间】:2018-03-26 09:00:20 【问题描述】:我已经实现了以下组件。它按预期工作和表现。尽管如此,由于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
)。
【问题讨论】:
你是如何使用这个组件的? 你读过angular.io/api/forms/ControlValueAccessor吗?Why is it important to execute the call to onChange(...) and onTouch(...) in writeValue(...)?
你在哪里看到的?
@yurzui,这里大概指的是these
@AngularInDepth.com 我想他指的是这个take.ms/IGcPJ
【参考方案1】:
阅读这篇文章,详细解释ControlValueAccessor
:
如果组件应该用作 Angular 表单的一部分,您通常需要在组件上实现 ControlValueAcceessor
接口。
我注释掉了 writeValue(...) 方法的第二行和第三行 而且,据我所知,没有任何损坏。
这可能是因为您没有应用任何表单指令 - formControl
或 ngModel
将 FormControl
链接到您的自定义输入组件。 FormControl
使用InputComponent
的writeValue
方法进行通信。
这是我上面引用的文章中的图片:
formControl
使用writeValue
方法为原生表单控件设置值。 registerOnChange
方法由formControl
使用来注册一个回调,该回调预计将在每次更新本机表单控件时触发。 registerOnTouched
方法用于指示用户与控件交互。
为什么执行对 onChange(...) 的调用很重要? 在 writeValue(...) 中的 onTouch(...)?什么会出错,在什么情况下 情况可以预期吗?
这是实现 ControlValueAcceessor
的自定义控件通知 Angular 的 FormControl
输入中的值已更改或用户与控件交互的机制。
...发现我什么都说不出来 去掉了 setDisabledState(...)...真的需要实现吗?
正如接口中指定的,当控件状态更改为“禁用”或从“禁用”更改时,表单 API 调用此函数。根据值,它应该启用或禁用适当的 DOM 元素。
如果您想在关联的FormControl
的状态变为disabled
时收到通知,则需要实现它,然后您可以执行一些自定义逻辑(例如,禁用您的输入组件)。
【讨论】:
很好的答案。不过有两个后续。 #1 如果我注册了onChange()
,但不从writeValue()
调用它(仅将值设置为支持字段但不将其传播到onChange()
),我仍然将值传递给父组件中的表单组.那么现在不工作的是什么?我看不到它。你有一个明确的例子说明什么是不可行的吗? #2 我看到setDisableState()
没有问号,方法名称后面有问号,括号后面有问号。是同一个意思吗?这是怎么回事?
@DonkeyBanana,请阅读我引用的文章,如果之后您仍有疑问,请创建一个 stackblitz 演示,让我知道文章中不清楚的地方
如果用户与您的 DOM 控件交互并更改其值,您必须调用 onChange
,以便将值写入表单值。在writeValue
中调用它是错误的。如果程序更改表单控件的值,表单将调用此函数。您只需更新您的 DOM 控件以反映当前的表单值。
我错过了一个明确的声明,在writeValue
中调用onChange
是错误的,因为必须使用onChange
来通知FormControl 值的更改,并且使用writeValue
更新用户界面。结合这些功能是一种循环依赖。您的回答一切正常。几天前我了解了 ControlValueAccessor 并偶然发现了 DonkeyBanana 引用的实现。所以我觉得有必要评论一下。
你让我摆脱了过去 3 天的极度痛苦。文章提供了深入的解释。谢谢!!【参考方案2】:
我不认为接受的答案以简洁的方式回答了最核心的问题:
为什么在 writeValue(...) 中调用 onChange(...) 和 onTouch(...) 很重要?
不是。您确实不需要在 writeValue 中调用 onChange。这不是预期用途(请参阅下面的文档链接)。
会出现什么问题以及在什么情况下会出现这种情况?
预计不会出错。详细回答:
如果您要从writeValue
内部调用onChange
,您会注意到:
registerOnChange
未调用)。
稍后从 writeValue 内部调用 onChange 会使用您从模型中获得的值更新您的模型(使用 emitModelToViewChange = false
以避免递归调用 writeValue)。
换句话说,除非您依赖您的组件以某种方式立即更改它在 writeValue 中接收到的值,并将这些更改传递回您的模型,但仅在 second 以及稍后调用 writeValue,您可以安全地不从 writeValue 调用 onChange。
在此处查看文档中的示例:https://angular.io/api/forms/ControlValueAccessor
【讨论】:
有趣的事实 - OP 提供的示例链接现在实际上在writeValue()
中都没有 this.onChange()
。
确实很有趣 - 真的是互联网课程更正了吗?
@corolla 我想根据不同的标准转换我传递给我的 formControl 的值。所以我需要调用WriteValue()中的onChange()方法。既然 registerOnChange() 在 writeValue() 之后触发,我该怎么做?以上是关于为啥在 Angular 中实现 ControlValueAccessor 时需要在 writeValue 中调用 onChange 和 onTouch?的主要内容,如果未能解决你的问题,请参考以下文章