订阅内的setValue后,FormGroup值不更新

时间:2019-07-11 12:35:10

标签: javascript angular rxjs

我有一个自定义单选列表,每个控件的控件名称不同(这些控件是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订阅中使用了false的setValue。

Stackblitz出现此错误:

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

  • Angular 7.2
  • RxJS 6.3

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

谢谢!

2 个答案:

答案 0 :(得分:2)

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

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 ??

答案 1 :(得分:1)

这本身并不能回答您问题的技术性,但是我在这里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>