繁体   English   中英

提交和值更改事件时的Angular 4 ControlValueAccessor值

[英]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-selectControlValueAccessor的实现或创建自己的组件来实现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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM