如何以编程方式激活“ value Changes”?

在我的一个页面上,我使用 FormBuilder在初始化时填充表单。每当输入为 没有空时,每个输入都获得一个类。这是通过向每个输入添加一个 ngClass,并订阅 FormGroupvalueChanges来完成的。

每当以编程方式填充表单时,就会出现问题。每当用户更改表单上的任何值时,就会调用 valueChanges,但是在使用 FormBuilder时情况并非如此。

我的问题是: 如何让 valueChanges事件在 FormBuilder完成时触发。

我试过使用 this.FormGroup.updateValueAndValidity(),但是没有任何结果。

75839 次浏览

我找到了一个适合我的解决方案。我忘记了 updateValueAndValidity函数中的两个参数。

  • 只会更新这个 FormControl时为真。
  • EmitEvent: 将导致在为 true 时触发 valueChanges事件。

所以你会得到这样的结果: FormGroup.updateValueAndValidity({ onlySelf: false, emitEvent: true });

你问题的题目和描述有点误导人。是哪一个? (i)

  • 在以编程方式更改字段值时,是否要更新字段的有效性状态?
  • 还是要确保在以编程方式更改字段值时调用对字段 valueChanges的订阅?

不管怎样,看看这个 Plunkr: https://plnkr.co/edit/4V4PUFI1D15ZDWBfm2hb?p=preview

当您像下面这样通过编程方式设置字段值时,您将看到:

this.myForm.get('myField').setValue(newValue);

更新了字段的有效性和可观察到的 valueChanges。所以,看起来你正在尝试重建一个已经存在的行为。

但是,当以编程方式更改字段值时,字段的 dirty属性不会更新(如 医生说的: “如果用户更改了值 用户界面,则控件是脏的”)。是不是您正在检查 dirty属性以指示字段错误,并且有效性状态没有更新的 幻觉,而实际上只有 dirty属性没有更新?

(i)这两件事是不同的。您不应该订阅 valueChanges来手动触发验证。验证应该通过在表单模型中声明验证器来实施。

你的问题不是100% 清楚,但我认为你的意思是:

我的 valueChanges 在更改 UI 中的某些内容时工作得很好,但是我想在完成构造函数中 FormBuilder 对象的初始化以处理初始条件后立即触发我的订阅函数逻辑。

在这种情况下,我要做的很简单:

    this.searchForm.valueChanges
.pipe(startWith(initialValues)).subscribe(value =>
{
// whatever you want to do here
});

initialValues是用来初始化表单的原始数据。

这只会导致可观察到的立即开火。


重要提示: 如果您使用异步管道而不是显式的 subscribe()来订阅,那么原始答案中存在一个微妙的问题。

在 Angular 中,您可以显式地订阅一个可观察的(如上所示) ,但是使用异步管道也同样常见。下面是一个例子:

model = {
firstName: this.searchForm.valueChanges.pipe(startWith(initialValues), map(values => values.firstName.toUpperCase())
};

然后在模板中显示表单值:

First name: \{\{ model.firstName | async | json }}

这种方法存在一个非常微妙的问题。

  • startWith操作符将捕获值 当你创建可观察的,这将是正确的第一次。
  • 然而,如果你重新订阅可观察到的,它仍然使用原始值,即使形式已经改变。如果 UI 被隐藏,然后再次显示(导致新的订阅) ,这可能会自动显示。

可能的解决办法:

  1. 使用 defer,使得每次订阅观察对象时,this.searchForm.value的值都会被计算出来。

    defer(() => this.searchForm.valueChanges.pipe(startWith(this.searchForm.value),
    map(values => values.firstName.toUpperCase())
    
  2. 使用 shareReplay。这只适用于 refCount: false,因此我 不建议这样做,因为它不会得到正确的清理。

    this.searchForm.valueChanges.pipe(startWith(initialValues),
    map(values => values.firstName.toUpperCase(),
    shareReplay({ refCount: false, bufferSize: 1 })
    
  3. 假设: 如果 startWith有一个 lambda,那么你可以使用它。不幸的是,它现在没有:

    startWith(() => this.searchForm.value)
    

注意: 如果您显式地使用 subscribe,那么就不需要担心这个问题。

我使用 patchValue重置表单中的值并以编程方式触发更改:

this.MyForm.controls["MyField"].patchValue(MyField);

订阅更改:

this.MyForm.get("MyField").valueChanges.subscribe(val => {
if (val) {
const MyField = this.MyForm.get("MyField");
}
});