Angular ReactiveForms:生成一组复选框值?

IT技术 javascript angular checkbox angular2-forms
2021-03-12 04:14:56

给定绑定到相同的复选框列表formControlName,如何生成绑定到 的复选框值数组formControl,而不是简单的true/ false

例子:

<form [formGroup]="checkboxGroup">
    <input type="checkbox" id="checkbox-1" value="value-1" formControlName="myValues" />
    <input type="checkbox" id="checkbox-2" value="value-2" formControlName="myValues" />
    <input type="checkbox" id="checkbox-3" value="value-2" formControlName="myValues" />
</form>

checkboxGroup.controls['myValues'].value 目前生产:

true or false

我希望它产生什么:

['value-1', 'value-2', ...]
6个回答

在silentsod answer的帮助下,我编写了一个解决方案来在我的formBuilder中获取值而不是状态。

我使用一种方法在 formArray 中添加或删除值。这可能是一个糟糕的方法,但它有效!

组件.html

<div *ngFor="let choice of checks; let i=index" class="col-md-2">
  <label>
    <input type="checkbox" [value]="choice.value" (change)="onCheckChange($event)">
    {{choice.description}}
  </label>
</div>

组件.ts

// For example, an array of choices
public checks: Array<ChoiceClass> = [
  {description: 'descr1', value: 'value1'},
  {description: "descr2", value: 'value2'},
  {description: "descr3", value: 'value3'}
];

initModelForm(): FormGroup{
  return this._fb.group({
    otherControls: [''],
    // The formArray, empty 
    myChoices: new FormArray([]),
  }
}

onCheckChange(event) {
  const formArray: FormArray = this.myForm.get('myChoices') as FormArray;

  /* Selected */
  if(event.target.checked){
    // Add a new control in the arrayForm
    formArray.push(new FormControl(event.target.value));
  }
  /* unselected */
  else{
    // find the unselected element
    let i: number = 0;

    formArray.controls.forEach((ctrl: FormControl) => {
      if(ctrl.value == event.target.value) {
        // Remove the unselected element from the arrayForm
        formArray.removeAt(i);
        return;
      }

      i++;
    });
  }
}

例如,当我提交表单时,我的模型如下所示:

  otherControls : "foo",
  myChoices : ['value1', 'value2']

只缺少一件事,如果您的模型已经检查了值,则缺少一个用于填充 formArray 的函数。

在此解决方案中,即使未选中复选框,表单也始终有效
2021-04-22 04:14:56
在使用您的示例进入数据库后加载数据时,如何检查我的复选框是否被选中?
2021-04-25 04:14:56
嘿,我设计了一个可扩展的面向对象的解决方案来解决这个用例,你可以在这里查看我的答案:stackoverflow.com/a/69637536/3317037
2021-04-25 04:14:56
myChoices: new FormArray([], Validators.required)
2021-04-29 04:14:56
“但它管用!” 这就是技术债务的开始方式。这不是做这件事的反应式方式。在每个复选框输入上使用表单控件的好处之一是,即使您将它们重新添加到 DOM 中,它们也可以记住它们的状态。
2021-05-14 04:14:56

这是使用https://angular.io/docs/ts/latest/api/forms/index/FormArray-class.html的好地方FormArray

首先,我们将使用 aFormBuilder或 new up a 来构建我们的控件数组FormArray

表单生成器

this.checkboxGroup = _fb.group({
  myValues: _fb.array([true, false, true])
});

新表格数组

let checkboxArray = new FormArray([
  new FormControl(true),
  new FormControl(false),
  new FormControl(true)]);

this.checkboxGroup = _fb.group({
  myValues: checkboxArray
});

很容易做到,但接下来我们将更改我们的模板,让模板引擎处理我们如何绑定到我们的控件:

模板.html

<form [formGroup]="checkboxGroup">
    <input *ngFor="let control of checkboxGroup.controls['myValues'].controls"
    type="checkbox" id="checkbox-1" value="value-1" [formControl]="control" />     
  </form>

这里我们循环访问我们的一套FormControls在我们myValues FormArray和每个控制我们结合[formControl]到控制,而不是在FormArray控制和<div>{{checkboxGroup.controls['myValues'].value}}</div>生产的true,false,true同时,也使您的模板语法有点不太手册。

你可以使用这个例子:http : //plnkr.co/edit/a9OdMAq2YIwQFo7gixbj?p=preview来看看

这很酷,但会产生一个完全通用的复选框数组。大概你会加载一个数组或其他东西,并将每个复选框与其他一些值相关联。例如,您如何将用于表单标签的文本字符串添加到每个表单控件?
2021-04-16 04:14:56
@Askdesigners 您可以发布您的解决方案以包含复选框和标签吗?
2021-04-17 04:14:56
for id 可以用于索引 *ngFor="let control of checkboxGroup.controls['myValues'].controls ; let i=index""
2021-04-22 04:14:56
NM 我刚刚将它映射到外部数组旁边:p
2021-04-25 04:14:56
可能你应该删除 id="xxx",id 应该是唯一的吧?
2021-05-01 04:14:56

与以前的版本相比,在 Angular 6 中执行此操作要容易得多,即使复选框信息是从 API 异步填充的。

首先要意识到的是,由于 Angular 6 的keyvalue管道,我们不再需要使用FormArray,而是可以嵌套FormGroup.

首先,将 FormBuilder 传递给构造函数

constructor(
    private _formBuilder: FormBuilder,
) { }

然后初始化我们的表单。

ngOnInit() {

    this.form = this._formBuilder.group({
        'checkboxes': this._formBuilder.group({}),
    });

}

当我们的复选框选项数据可用时,对其进行迭代,我们可以将其FormGroup作为 named直接推送到嵌套FormControl,而不必依赖数字索引查找数组。

const checkboxes = <FormGroup>this.form.get('checkboxes');
options.forEach((option: any) => {
    checkboxes.addControl(option.title, new FormControl(true));
});

最后,在模板中,我们只需要迭代keyvalue复选框的 : 没有附加let index = i,复选框将自动按字母顺序排列:更干净。

<form [formGroup]="form">

    <h3>Options</h3>

    <div formGroupName="checkboxes">

        <ul>
            <li *ngFor="let item of form.get('checkboxes').value | keyvalue">
                <label>
                    <input type="checkbox" [formControlName]="item.key" [value]="item.value" /> {{ item.key }}
                </label>
            </li>
        </ul>

    </div>

</form>
@f.khantsis 你可以这样做:` const value = { key1: true, key2: false, key3: true }; const list = Object.entries(value).filter(([_, isSelected]) => isSelected).map(([key]) => key); 控制台日志(列表);`
2021-04-15 04:14:56
最好的解决方案恕我直言。您可以将分配const checkboxes = ..放在 foreach 之外;)
2021-04-23 04:14:56
这个解决方案看起来不错而且干净,但我遇到了一个错误:“未知”类型不能分配给“数字”类型。<input type="checkbox" [formControlName]="item.key" [value]="item.value" /> {{ item.key }}
2021-04-24 04:14:56
这仍然摘录 [key1=true,key2=false,key3=true]。我们想要 ['key1','key3']
2021-05-04 04:14:56
在复选框值的简单硬编码数组的情况下也非常有用。然后,您可以立即在 ngOnInit() 中使用类似的 for 循环添加表单控件,表单中的复选框将动态反映复选框值数组
2021-05-07 04:14:56

我在这里没有看到一个解决方案可以最大程度地使用react形式完全回答问题,所以这是我的解决方案。


概括

这是详细解释的要点以及 StackBlitz 示例。

  1. 使用FormArray该复选框并初始化形式。
  2. valueChanges当您希望表单显示某些内容但在组件中存储其他内容时observable 是完美的选择。true/false映射到此处所需的值。
  3. 过滤掉false提交时的值。
  4. 取消订阅valueChangesobservable。

StackBlitz 示例


详细说明

使用 FormArray 定义表单

正如在标记为正确的答案中已经提到的那样。FormArray在您希望以数组形式获取数据的情况下,这是一种可行的方法。因此,您需要做的第一件事就是创建表单。

checkboxGroup: FormGroup;
checkboxes = [{
    name: 'Value 1',
    value: 'value-1'
}, {
    name: 'Value 2',
    value: 'value-2'
}];

this.checkboxGroup = this.fb.group({
    checkboxes: this.fb.array(this.checkboxes.map(x => false))
});

这只会将所有复选框的初始值设置为false

接下来,我们需要在模板中注册这些表单变量并遍历checkboxes数组(不是FormArray复选框数据)以在模板中显示它们。

<form [formGroup]="checkboxGroup">
    <ng-container *ngFor="let checkbox of checkboxes; let i = index" formArrayName="checkboxes">
        <input type="checkbox" [formControlName]="i" />{{checkbox.name}}
    </ng-container>
</form>

使用 valueChanges 可观察

这是我在这里给出的任何答案中都没有提到的部分。在这种情况下,我们希望显示所述数据但将其存储为其他内容,valueChanges可观察对象非常有用。使用valueChanges,我们可以观察到在变化checkboxes,然后maptrue/false从接收到的值FormArray,以所期望的数据。请注意,这不会更改复选框的选择,因为传递给复选框的任何真实值都会将其标记为已选中,反之亦然。

subscription: Subscription;

const checkboxControl = (this.checkboxGroup.controls.checkboxes as FormArray);
this.subscription = checkboxControl.valueChanges.subscribe(checkbox => {
    checkboxControl.setValue(
        checkboxControl.value.map((value, i) => value ? this.checkboxes[i].value : false),
        { emitEvent: false }
    );
});

这基本上将FormArray映射到原始checkboxes数组,并value在复选框标记为 的情况下true返回 ,否则返回falseemitEvent: false这里很重要,因为设置FormArray没有它值将导致 valueChanges发出一个创建无限循环的事件。通过设置emitEventfalse,我们确保在valueChanges此处设置值时 observable 不会发出。

过滤掉错误值

我们不能直接过滤 中的false值,FormArray因为这样做会弄乱模板,因为它们绑定到复选框。所以最好的解决方案是false在提交过程中过滤掉这些值。使用扩展运算符来执行此操作。

submit() {
    const checkboxControl = (this.checkboxGroup.controls.checkboxes as FormArray);
    const formValue = {
        ...this.checkboxGroup.value,
        checkboxes: checkboxControl.value.filter(value => !!value)
    }
    // Submit formValue here instead of this.checkboxGroup.value as it contains the filtered data
}

这基本上过滤掉falsy从值checkboxes

取消订阅 valueChanges

最后别忘了退订 valueChanges

ngOnDestroy() {
    this.subscription.unsubscribe();
}

注意:有一种特殊情况,值不能设置为FormArrayin valueChanges,即如果复选框值设置为 number 0这将使该复选框看起来无法选中,因为选择该复选框会将 设置FormControl为数字0(假值),因此保持未选中状态。最好不要将数字0用作值,但如果需要,您必须有条件地设置0为某个真实值,例如字符串'0'或只是普通值,true然后在提交时将其转换回数字0

StackBlitz 示例

StackBlitz 也有代码用于何时您希望将默认值传递给复选框,以便在 UI 中将它们标记为已选中。

这仍然需要维护两个数组并使它们保持同步。仍然没有我希望的那么干净。也许我们可以让表单控件保存复数值而不是两个数组。
2021-04-16 04:14:56
复数值不起作用,因为复选框的值必须为 true 或 false。所以这个解决方案看起来仍然是最好的。
2021-04-25 04:14:56

如果您正在寻找 JSON 格式的复选框值

{ "name": "", "countries": [ { "US": true }, { "Germany": true }, { "France": true } ] }

完整示例在这里

对于使用国家名称作为复选框值而不是问题中的值,我深表歉意。进一步说明——

为表单创建一个 FormGroup

 createForm() {

    //Form Group for a Hero Form
    this.heroForm = this.fb.group({
      name: '',
      countries: this.fb.array([])
    });

    let countries=['US','Germany','France'];

    this.setCountries(countries);}
 }

让每个复选框成为从一个对象构建的 FormGroup,该对象的唯一属性是复选框的值。

 setCountries(countries:string[]) {

    //One Form Group for one country
    const countriesFGs = countries.map(country =>{
            let obj={};obj[country]=true;
            return this.fb.group(obj)
    });

    const countryFormArray = this.fb.array(countriesFGs);
    this.heroForm.setControl('countries', countryFormArray);
  }

复选框的 FormGroups 数组用于设置父表单中“国家/地区”的控件。

  get countries(): FormArray {
      return this.heroForm.get('countries') as FormArray;
  };

在模板中,使用管道获取复选框控件的名称

  <div formArrayName="countries" class="well well-lg">
      <div *ngFor="let country of countries.controls; let i=index" [formGroupName]="i" >
          <div *ngFor="let key of country.controls | mapToKeys" >
              <input type="checkbox" formControlName="{{key.key}}">{{key.key}}
          </div>
      </div>
  </div>
你们需要从示例链接中复制管道 mapToKeys,如果该链接失效,则如下所示。您可能需要删除第二个参数(args)并在应用程序module文件中声明管道: import {Pipe, PipeTransform} from '@angular/core'; @Pipe({name: 'mapToKeys'}) 导出类 MapToKeysPipe 实现 PipeTransform { transform(value) : any { let keys = []; for (let key in value) { keys.push({key: key, value: value[key]}); } 返回键;} }
2021-05-03 04:14:56