表单控件 valueChanges 给出之前的值

Posted

技术标签:

【中文标题】表单控件 valueChanges 给出之前的值【英文标题】:Form control valueChanges gives the previous value 【发布时间】:2017-12-07 10:57:39 【问题描述】:

我在表单对象parentForm 中有一个名称为'question1' 的表单控件,我已通过以下方式订阅它。

它是一个单选按钮,有两个选项 YesNo,当我选择 No 时,我得到 Yes,当我选择 Yes 时,它是 No

this.parentForm.controls['question1'].valueChanges.subscribe(
  (selectedValue) => 
    // If option `No is selected`
    console.log(selectedValue);  // displays No. OK
    console.log(this.parentForm.value['question1']);  // displays Yes. Problem is here
  
);

selectedValue 变量具有正确的值,但如果我这样做 console.log(this.parentForm.value['question1'] 它会给出以前的值。

在从this.parentForm.value['question1'] 检索值之前,我尝试输入setTimeout(),它工作正常。

setTimeout(() => 
  console.log(this.parentForm.value['question1']); // gives the correct value.
, 500);

但我的问题是为什么 parentForm 在其控件的值更改时不会更新,而且我也仅在值更改后才检索其值。

注意:我不想观察parentForm.valueChanges,不是我的要求。

【问题讨论】:

那么是什么阻止您使用selectedValue 使用 this.parentForm.value['question1'] 我可以在组件内的任何位置访问它。 selectedValue 仅具有本地范围。 【参考方案1】:

valueChanges 事件在 新值更新为 FormControl 值之后触发,之前 将更改冒泡到其父级和祖先级。因此,您必须访问 FormControl 本身的值(刚刚修补),而不是 FormGroup 值对象的字段(在事件期间未触及)。

鉴于此,请改用this.parentForm.get('question1').value

this.parentForm.controls['question1'].valueChanges.subscribe(
    (selectedValue) => 
      console.log(selectedValue);
      console.log(this.parentForm.get('question1').value);     
    
);

【讨论】:

谢谢!这很完美。知道为什么其他方法不起作用吗? @AmitChigadani 我已经更新了我的答案以进行一些解释,请看一下。 这里描述的功能被标记为一个错误:github.com/angular/angular/issues/13129 RxJS 操作者可以管理订阅结果并提取 prev 和 next 而无需使用表单控件值:***.com/a/54205373/1148107【参考方案2】:

还有一个选项,但它可能并不适用于所有情况:您可以等待 one tick 在 FormControl 中更新 selectedValue 之前:

setTimeout( () => console.log( selectedValue ) );

有关同步/异步表单主题的更多信息:Angular Guide 文档中的Reactive forms。

【讨论】:

如果不是真的需要,请不要使用 setTimeout,这里也不需要【参考方案3】:

尝试这样做

this.parentForm.controls['question1'].valueChanges.subscribe(
    (selectedValue) => 
      console.log(selectedValue);
      console.log(this.parentForm.value.question1);     
    
);

如果 FormBuilder 的控件被更新,您可以立即通过属性值从 FormBuilder 对象中恢复最后的值

【讨论】:

这和accepted answer非常相似。 这不应该被依赖,因为它是利用 Angular 更改传播方式的黑客。请使用 RxJS 成对操作符【参考方案4】:

valueChanges 是一个 Observable,因此您可以通过管道 pairwise 获取订阅中的上一个和下一个值。

// No initial value. Will emit only after second character entered
this.form.get('fieldName')
  .valueChanges
  .pipe(pairwise())
  .subscribe(([prev, next]: [any, any]) => ... );
// Fill buffer with initial value, and it will emit immediately on value change
this.form.get('fieldName')
  .valueChanges
  .pipe(startWith(null), pairwise())
  .subscribe(([prev, next]: [any, any]) => ... );

在 StackBlitz 中工作的示例: https://stackblitz.com/edit/angular-reactive-forms-vhtxua

更新

如果您注意到startWith 似乎已已弃用,情况并非如此。操作员只有一个活动签名,您可以在此answer 中阅读。

很有可能,您正在使用 startWith(null) 或 startWith(undefined),尽管有通知,但它们并未被弃用,但 IDE 检测到错误的函数签名已被弃用,并显示警告。

一个简单的解决方法是提供预期的返回类型:

// Prevent deprecation notice when using `startWith` since it has not been deprecated
this.form.get('fieldName')
  .valueChanges
  .pipe(startWith(null as string), pairwise())
  .subscribe(([prev, next]: [any, any]) => ... );

使用 startWith 在 StackBlitz 中工作的示例: https://stackblitz.com/edit/angular-reactive-forms-rvxiua

【讨论】:

成对应该是答案。 使用pairwise() 删除字符时,订阅似乎没有触发。示例:我的输入框当前为“96”,然后我将“6”删除为输入框中的“9”,但 valueChanges.pipe.subscribe 块不执行。 @rharrison33 我不确定你发生了什么,但如果你查看我为答案构建的 StackBlitz,它会删除并触发值更改。 startWith 似乎已弃用,截至我发表评论时,解决方案仍然可以正常工作 我已经更新了答案,以防止误认为 startWith 已被弃用,并提供了 *** 上的答案的链接和引用,因此这个答案仍然是主题,不会出现不必要的膨胀。

以上是关于表单控件 valueChanges 给出之前的值的主要内容,如果未能解决你的问题,请参考以下文章

启用/禁用表单控件触发 valueChanges Angular 2 Forms

FormGroup.patchValue 保持子控件的禁用状态

如何查看除特定控件之外的所有 FormControls ValueChanges?

Angular 5 - 反应形式

我如何防止剑道下拉列表在角度 4+ 中更改 valueChange 或 selectionChange 事件的值

Angular2自定义表单控件防止发射事件