【问题标题】:FormGroup values are not updated after setValue inside subscription订阅内的 setValue 后未更新 FormGroup 值
【发布时间】:2019-11-21 04:21:02
【问题描述】:

我有自定义单选列表,每个控件都有不同的控件名称(这些控件是 html 中的复选框)认为来自这些复选框的列表(输入名称)只能选择一个,我做这样的事情(在 ngOnInit 中) :

formGroup
  .get('ChildGroup')
  .valueChanges.pipe(
    startWith(formGroup.get('ChildGroup').getRawValue()),
    pairwise(),
  )
  .subscribe(([oldState, newState]) => {
    console.log(oldState, newState)
    // List of radio checkboxes
    const radioInputs = [
      'childName1', 'childName2', 'childName3', 'childName4',
    ];
    // Find changed value
    const updatedElementName = radioInputs.find(
      elem => oldState[elem] !== newState[elem],
    );
    // Set true only for input which is changed
    radioInputs.forEach(input => {
      formGroup
        .get('ChildGroup')
        .get(input)
        .setValue(
          updatedElementName === input, { emitEvent: false },
        );
    });
  });

问题很奇怪,因为在单击复选框后,我从日志中收到了这样的数据:

// First click on childName2 (childName1 unchecked) - correct, expected
oldState.childName1 // true
oldState.childName2 // false
newState.childName1 // true
newState.childName2 // true

// Second click on childName1 (childName1 and childName2 unchecked)
oldState.childName1 // true
oldState.childName2 // true
newState.childName1 // true
newState.childName2 // true

oldState.childName1 在第二次点击中的值并不像预期的那样为假,即使我在第一次点击后在 valueChanges 订阅中使用了 setValue on false。

存在此错误的 Stackblitz:

https://stackblitz.com/edit/angular-q9ppdt

  • Angular 7.2
  • RxJS 6.3

如果我的描述不清楚,请告诉我。

谢谢!

【问题讨论】:

标签: javascript angular rxjs


【解决方案1】:

这可以是一个简单的解决方案,只需订阅每个控件的值更改并翻转另一个控件的值

this.formGroup.get('control1').valueChanges.subscribe(value => {
  this.formGroup.get('control2').setValue(!value, { emitEvent: false })
})

this.formGroup.get('control2').valueChanges.subscribe(value => {
  this.formGroup.get('control1').setValue(!value, { emitEvent: false })
})

demo ??

更新了!?? 与上述相同的思想基础,但作为控件数组的动态订阅基础

如果我有这个控件列表

  radioInputs = [
    'control1', 'control2', 'control3', 'control4', 'control5'
  ];

订阅每个表单控件,每次选中的任何控件都会重置其他控件

this.radioInputs.forEach(control => {
  this.formGroup.get(control).valueChanges.subscribe(value => {
    if (value) {
      this.resetOthers(control);
    }
  })
});

重置方法

  resetOthers(currentControl) {
    this.radioInputs.filter(control => control !== currentControl).forEach(control => {
      this.formGroup.get(control).setValue(false, { emitEvent: false })
    })
  }

demo ??

【讨论】:

  • 如果我们想取消选择所有选项怎么办?
  • @JoeyGough 你可以使用reset方法,我已经更新了demo
  • 扩展到四个或五个选项的难易程度如何?
  • @JoeyGough 您的主要目标是切换其他控件权限,例如第一个为真所有其他为假?
  • @JoeyGough 如果五个被取消选中,一个会触发另一个被选中?
【解决方案2】:

这本身并不能回答您问题的技术性问题,但我实现了您在此处尝试实现的目标https://stackblitz.com/edit/angular-s1vj53

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { mergeMap, pairwise, startWith, take } from 'rxjs/operators';

interface RadioControl {
  title: string;
  initialValue: boolean;
}

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit  {
  name = 'Angular';
  formGroup: FormGroup

  public controls: RadioControl[] = [
    {
      title: 'control1',
      initialValue: true
    },
    {
      title: 'control2',
      initialValue: false
    },
    {
      title: 'control3',
      initialValue: false
    },
    {
      title: 'control4',
      initialValue: false
    }
  ]

  constructor(
    private fb: FormBuilder
  ) {

  }
  handleInputClick(controlName: string): void {
    // remove this if block if you wish not allow all blank inputs and always have one selected
    // This allows to unselect all
    if (this.formGroup.get(controlName).value) {
      this.formGroup.get(controlName).setValue(false);
      return;
    }
    Object.keys(this.formGroup.controls).forEach((e: string) => {
      this.formGroup.get(e).setValue(e === controlName);
    });
  }
  ngOnInit() {
    const groupInitObj = {}
    this.controls.forEach((e: RadioControl) => {
      groupInitObj[e.title] = this.fb.control(e.initialValue);
    });
    this.formGroup = this.fb.group(groupInitObj);
  }
}
<form [formGroup]="formGroup">
  <input *ngFor="let control of controls" (click)="handleInputClick(control.title)" type="checkbox" [formControlName]="control.title">
</form>

【讨论】:

  • ? 我们的两个答案都与我使用 valueChange 的相同想法有关,您只需添加点击方法
猜你喜欢
  • 2021-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多