![](/img/trans.png)
[英]Angular ControlValueAccessor with default value from internal causes dirty
[英]Angular 4 ControlValueAccessor value on submit and value changes events
在我的Angular(4 + Typescript)项目中,我使用了已经准备好的组件(因为扩展ControlValueAccessor的方式是相同的,这并不重要,但是如果这很重要,我使用ng2-select),这是我想要的,因为它具有更好的UX和更好的用户体验看。 在将其包含到使用响应式表单的表单组件中之后,提交后,我基于准备好的组件内部使用的类SelectItem返回(用于表单控件)对象,以管理选定的项-像这样:
// console.log(this.myForm.values)
Object {
txtInput: 'value',
// ...
preparedComponent: SelectItem { // <--
id: '1',
text: 'Chosen item'
}
}
由于该组件基本上只是标准select控件的替代品,因此我期望我将获取以字符串形式形成的选定项的ID,如下所示:
// console.log(this.myForm.values)
Object {
txtInput: 'value',
// ...
preparedComponent: '1' // <--
}
在了解了ControlValueAccessor的实际含义和工作原理之后,我将提交(或更好地说说项目更改onChange
)返回的内容更改为项目ID。 这个问题解决了一段时间,直到我开始设置预定义值(在编辑实例上)以形成控件而不是空值(在添加实例上)为止。 我发现这一次writeValue
基于作为预定义值发送给它的内容来分配值以形成表单控件。 因此,如果我将预定义值发送为...
Object {
id: '1',
text: 'Chosen item'
}
就像上面的第一个代码示例一样,这也是对象提交时的值。 我为此做了一个变通方法,即在调用writeValue
之后立即分配正确的值以使用onChange
进行控制。 这确实是不正确的方式,但除此以外,我一无所知:
public writeValue(val: any): void {
this.selectedItems = val; // val is for example Object { id: '1', text: 'Chosen item' }
setTimeout(() => {
this.onChange(val.id); // just an example of emitted value, there should be some checks val is object and id exists in real code
}, 0);
}
就我最初不是订阅表单控件的valueChanges
,这也起作用。 问题在于,每次为准备好的组件分配预定义的值时,即使在使用{ emitEvent: false }
也会在提交后将使用过的值发出给用户。
所以这就是我想用准备好的组件处理的事情,因为我想从提交时的控件中获取正确的值,并且我不希望在值没有真正改变之前将更改发送给订阅者,而不仅仅是将其替换为预定义值。 任何想法如何处理将不胜感激。
我不确定我是否正确理解了这一点,但是我想尝试一下。
您希望组件的输入为以下类型:
{
id: string,
text: string
}
但是您希望组件的输出只是所选项目的字符串ID。
我认为这种数据流有点尴尬。 在初始化期间, preparedComponent
是一个object
,但是当选择一个项目时,您要将其变成string
。 除了类型安全性问题之外,它只是感觉不直观。
为了进行比较,请想象使用ngModel
和文本输入执行此ngModel
:
<input type="text" [(ngModel)]="preparedComponent" />
您希望传递一个对象,并在用户键入内容时返回一个字符串吗? 可能不会。
但是,由于某种原因,我们假设它一定是这样的。 您可以通过修改ng2-select
的ControlValueAccessor
的实现或创建自己的组件来实现ControlValueAccessor
并包装ng2-select
。 (我无法确定您在问题中正在执行哪一个操作)。 现在,当选择一个项目时,将调用this.onChange(val.id)
,该项目将使用字符串ID更新父组件。
然后,您会注意到一个单独的问题,即如果您预先定义了选定的值,则ng2-select
会在初始化期间触发更改事件。 这很奇怪,因为用户尚未与选择控件交互。 所以,你试图初始化preparedComponent
通过调用setValue
与你的表单控件emitEvent: false
,但我猜这并没有工作,因为虽然它可能是从当你初始化你的数据被发射防止更改事件, ng2-select
由第二立即更改您的数据。
因此,您可以通过等待直到ng2-select
第一次更新将父组件更新为新对象,然后(使用setTimeout
)用您实际想要的字符串ID覆盖它来解决此问题。 这使preparedComponent
使用字符串保持最新状态,但是即使用户未与控件进行交互,也不能解决更改事件触发的问题。 (顺便说一句,如果您用自己的组件包装ng2-select
,那么我不确定为什么要执行此setTimeout
部分,因为您还没有另一个调用this.onChange(val.id)
当ng2-select
通知您一个新的选定项目时,正如我在上一段中提到的那样?
由于我无法确定您是直接修改ng2-select
还是使用自己的组件包装它:
如果要修改ng2-select
,为什么不只找到其writeValue
方法并删除发出事件的代码呢? 这样,当您预定义值时,它将更新DOM,但不会随后覆盖您的值。
如果ng2-select
自己的组件包装ng2-select
,为什么不修改ng2-select
即将触发事件的writeValue
方法来存储标志,因此可以忽略它:
private: ignoreSelectedEvent = false;
public writeValue(val: any): void {
this.ignoreSelectedEvent = true;
// Update ng2-select, which will cause an event to fire
}
然后在对新选择的项目做出反应的函数中:
if (this.ignoreSelectedEvent) {
this.ignoreSelectedEvent = false;
return;
}
// Update the model with the string ID
this.onChange(val.id);
基本上,问题是询问是否可以将不同的值类型作为初始值发送给控件组件,该值由writeValue方法处理。 然后,应根据选择的类型将值转换为字符串ID或字符串ID数组-单个值还是多个值。 正如弗兰克在回答中所说,这样做和用setTimeout解决方法调用onChange有点尴尬。
因此,解决方案是将初始值设置为控制组件在将某些对象转换为ID / ID数组后在onChange中返回的值。 组件本身必须根据发送到其中的一个或多个ID从所有可用项目中选择适当的对象。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.