同じ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', ...]
ここにFormArray
name__を使用するのに適した場所があります https://angular.io/docs/ts/latest/api/forms/index/FormArray-class.html
まず、コントロールの配列をFormBuilder
name__で構築するか、FormArray
name__を更新します。
FormBuilder
this.checkboxGroup = _fb.group({
myValues: _fb.array([true, false, true])
});
新しいFormArray
let checkboxArray = new FormArray([
new FormControl(true),
new FormControl(false),
new FormControl(true)]);
this.checkboxGroup = _fb.group({
myValues: checkboxArray
});
簡単ですが、テンプレートを変更し、テンプレートエンジンにコントロールへのバインド方法を処理させます。
template.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
name__のFormArray
name__のセットを繰り返し処理し、各コントロールに対して、FormArray
name__コントロールの代わりに[formControl]
をバインドし、<div>{{checkboxGroup.controls['myValues'].value}}</div>
がtrue,false,true
を生成しますテンプレートの構文を手動で作成するのを少し減らします。
この例を使用できます: http://plnkr.co/edit/a9OdMAq2YIwQFo7gixbj?p=preview
私は私のformBuilderの状態の代わりに値を取得するためのソリューションを書いた。
FormArrayの値を追加または削除するためのメソッドを使用します。それは悪いアプローチかもしれませんが、うまくいきます!
component.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>
component.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']
1つだけ不足しているものがあります。モデルがすでに値をチェックしている場合にformArrayを埋める関数です。
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);
}
チェックボックスのFormGroupの配列は、親フォームの「国」のコントロールを設定するために使用されます。
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>
チェックボックス情報がAPIから非同期で取り込まれる場合でも、以前のバージョンよりもAngular 6でこれを実行する方がはるかに簡単です。
最初に気づくべきことは、Angular 6のkeyvalue
パイプのおかげでもうFormArray
を使う必要がなくなり、代わりにFormGroup
をネストすることができるということです。
まず、FormBuilderをコンストラクタに渡します。
constructor(
private _formBuilder: FormBuilder,
) { }
それから私たちのフォームを初期化します。
ngOnInit() {
this.form = this._formBuilder.group({
'checkboxes': this._formBuilder.group({}),
});
}
チェックボックスオプションのデータがある場合は、それを繰り返すと、番号付きのルックアップ配列に頼ることなく、名前付きFormGroup
としてネストされたFormControl
に直接プッシュできます。
options.forEach((option: any) => {
const checkboxes = <FormGroup>this.form.get('checkboxes');
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>
クリックされたときにイベントを作成し、手動でtrueの値をチェックボックスが表すものの名前に変更すると、nameまたはtrueが同じ値を評価し、true/falseのリストの代わりにすべての値を取得できます。例:
component.html
<form [formGroup]="customForm" (ngSubmit)="onSubmit()">
<div class="form-group" *ngFor="let parameter of parameters"> <!--I iterate here to list all my checkboxes -->
<label class="control-label" for="{{parameter.Title}}"> {{parameter.Title}} </label>
<div class="checkbox">
<input
type="checkbox"
id="{{parameter.Title}}"
formControlName="{{parameter.Title}}"
(change)="onCheckboxChange($event)"
> <!-- ^^THIS^^ is the important part -->
</div>
</div>
</form>
component.ts
onCheckboxChange(event) {
//We want to get back what the name of the checkbox represents, so I'm intercepting the event and
//manually changing the value from true to the name of what is being checked.
//check if the value is true first, if it is then change it to the name of the value
//this way when it's set to false it will skip over this and make it false, thus unchecking
//the box
if(this.customForm.get(event.target.id).value) {
this.customForm.patchValue({[event.target.id] : event.target.id}); //make sure to have the square brackets
}
}
これは、Angular Formsによって既にtrueまたはfalseに変更された後のイベントをキャッチします。trueの場合は、チェックボックスが表すものの名前に名前を変更します。 true/falseもチェックしました。
TL; DR
これも時々私を驚かせたので、FormArrayとFormGroupの両方のアプローチを試しました。
ほとんどの場合、チェックボックスのリストがサーバーに入力され、APIを介して受信しました。ただし、事前定義された値を持つ静的なチェックボックスのセットがある場合があります。ユースケースごとに、対応するFormArrayまたはFormGroupが使用されます。
基本的に
FormArray
はFormGroup
のバリアントです。主な違いは、データが配列としてシリアル化されることです(FormGroupの場合はオブジェクトとしてシリアル化されるのとは対照的です)。これは、動的フォームなど、グループ内に存在するコントロールの数がわからない場合に特に便利です。
簡単にするために、次のような単純な製品作成フォームがあるとします。
最初に、製品名formControlのみでフォームを設定します。これは必須フィールドです。
this.form = this.formBuilder.group({
name: ["", Validators.required]
});
カテゴリは動的にレンダリングされるため、データの準備が整った後、これらのデータをフォームに追加する必要があります。
this.getCategories().subscribe(categories => {
this.form.addControl("categoriesFormArr", this.buildCategoryFormArr(categories));
this.form.addControl("categoriesFormGroup", this.buildCategoryFormGroup(categories));
})
カテゴリリストを作成するには、2つの方法があります。
buildCategoryFormArr(categories: ProductCategory[], selectedCategoryIds: string[] = []): FormArray {
const controlArr = categories.map(category => {
let isSelected = selectedCategoryIds.some(id => id === category.id);
return this.formBuilder.control(isSelected);
})
return this.formBuilder.array(controlArr, atLeastOneCheckboxCheckedValidator())
}
<div *ngFor="let control of categoriesFormArr?.controls; let i = index" class="checkbox">
<label><input type="checkbox" [formControl]="control" />
{{ categories[i]?.title }}
</label>
</div>
このbuildCategoryFormGroup
はFormArrayを返します。また、選択した値のリストを引数として取るため、データを編集するためにフォームを再利用したい場合に役立ちます。新しい製品フォームを作成する目的では、まだ適用できません。
FormArray値にアクセスしようとすると注意してください。 [false, true, true]
のようになります。選択したIDのリストを取得するには、リストからチェックするためにもう少し作業が必要でしたが、配列インデックスに基づいています。私には良く聞こえませんが、動作します。
get categoriesFormArraySelectedIds(): string[] {
return this.categories
.filter((cat, catIdx) => this.categoriesFormArr.controls.some((control, controlIdx) => catIdx === controlIdx && control.value))
.map(cat => cat.id);
}
そのため、私はFormGroup
を使用して思いついたのです
FormGroupの違いは、フォームデータをオブジェクトとして保存することです。これには、キーとフォームコントロールが必要です。そのため、キーをcategoryIdとして設定し、後で取得することをお勧めします。
buildCategoryFormGroup(categories: ProductCategory[], selectedCategoryIds: string[] = []): FormGroup {
let group = this.formBuilder.group({}, {
validators: atLeastOneCheckboxCheckedValidator()
});
categories.forEach(category => {
let isSelected = selectedCategoryIds.some(id => id === category.id);
group.addControl(category.id, this.formBuilder.control(isSelected));
})
return group;
}
<div *ngFor="let item of categories; let i = index" class="checkbox">
<label><input type="checkbox" [formControl]="categoriesFormGroup?.controls[item.id]" /> {{ categories[i]?.title }}
</label>
</div>
フォームグループの値は次のようになります。
{
"category1": false,
"category2": true,
"category3": true,
}
ただし、ほとんどの場合、categoryIdsのリストのみを["category2", "category3"]
として取得します。また、これらのデータを取得するgetを作成する必要があります。実際にフォーム自体から値を取得できるので、formArrayと比較してこのアプローチの方が好きです。
get categoriesFormGroupSelectedIds(): string[] {
let ids: string[] = [];
for (var key in this.categoriesFormGroup.controls) {
if (this.categoriesFormGroup.controls[key].value) {
ids.Push(key);
}
else {
ids = ids.filter(id => id !== key);
}
}
return ids;
}
少なくともXチェックボックスが選択されていることを確認するバリデータを作成しました。デフォルトでは、1つのチェックボックスに対してのみチェックします。
export function atLeastOneCheckboxCheckedValidator(minRequired = 1): ValidatorFn {
return function validate(formGroup: FormGroup) {
let checked = 0;
Object.keys(formGroup.controls).forEach(key => {
const control = formGroup.controls[key];
if (control.value === true) {
checked++;
}
});
if (checked < minRequired) {
return {
requireCheckboxToBeChecked: true,
};
}
return null;
};
}
Angularリアクティブフォーム( https://angular.io/guide/reactive-forms )を使用する場合.
1つのフォームコントロールを使用して、チェックボックスグループの出力値を管理できます。
コンポーネント
import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
import { flow } from 'lodash';
import { flatMap, filter } from 'lodash/fp';
@Component({
selector: 'multi-checkbox',
templateUrl: './multi-checkbox.layout.html',
})
export class MultiChecboxComponent {
checklistState = [
{
label: 'Frodo Baggins',
value: 'frodo_baggins',
checked: false
},
{
label: 'Samwise Gamgee',
value: 'samwise_gamgee',
checked: true,
},
{
label: 'Merry Brandybuck',
value: 'merry_brandybuck',
checked: false
}
];
form = new FormGroup({
checklist : new FormControl(this.flattenValues(this.checklistState)),
});
checklist = this.form.get('checklist');
onChecklistChange(checked, checkbox) {
checkbox.checked = checked;
this.checklist.setValue(this.flattenValues(this.checklistState));
}
flattenValues(checkboxes) {
const flattenedValues = flow([
filter(checkbox => checkbox.checked),
flatMap(checkbox => checkbox.value )
])(checkboxes)
return flattenedValues.join(',');
}
}
html
<form [formGroup]="form">
<label *ngFor="let checkbox of checklistState" class="checkbox-control">
<input type="checkbox" (change)="onChecklistChange($event.target.checked, checkbox)" [checked]="checkbox.checked" [value]="checkbox.value" /> {{ checkbox.label }}
</label>
</form>
checklistState
チェックリスト入力のモデル/状態を管理します。このモデルはあなたが現在の状態をあなたが必要とするどんな値フォーマットにもマッピングすることを可能にします。
モデル:
{
label: 'Value 1',
value: 'value_1',
checked: false
},
{
label: 'Samwise Gamgee',
value: 'samwise_gamgee',
checked: true,
},
{
label: 'Merry Brandybuck',
value: 'merry_brandybuck',
checked: false
}
checklist
フォームコントロールこのコントロールは値を保存したい値を保存します。
値の出力:"value_1,value_2"
https://stackblitz.com/edit/angular-multi-checklist でデモを参照してください
テンプレートの一部: -
<div class="form-group">
<label for="options">Options:</label>
<div *ngFor="let option of options">
<label>
<input type="checkbox"
name="options"
value="{{option.value}}"
[(ngModel)]="option.checked"
/>
{{option.name}}
</label>
</div>
<br/>
<button (click)="getselectedOptions()" >Get Selected Items</button>
</div>
コントローラー部: -
export class Angular2NgFor {
constructor() {
this.options = [
{name:'OptionA', value:'first_opt', checked:true},
{name:'OptionB', value:'second_opt', checked:false},
{name:'OptionC', value:'third_opt', checked:true}
];
this.getselectedOptions = function() {
alert(this.options
.filter(opt => opt.checked)
.map(opt => opt.value));
}
}
}
私の解決策 - Material ViewでAngular 5を解決しました
つながりは、
formArrayName = "通知"
(変更)= "updateChkbxArray(n.id、$ event.checked、 'notification')"
このようにして、1つの形式で複数のチェックボックス配列に対して機能することができます。毎回接続するコントロール配列の名前を設定するだけです。
constructor(
private fb: FormBuilder,
private http: Http,
private codeTableService: CodeTablesService) {
this.codeTableService.getnotifications().subscribe(response => {
this.notifications = response;
})
...
}
createForm() {
this.form = this.fb.group({
notification: this.fb.array([])...
});
}
ngOnInit() {
this.createForm();
}
updateChkbxArray(id, isChecked, key) {
const chkArray = < FormArray > this.form.get(key);
if (isChecked) {
chkArray.Push(new FormControl(id));
} else {
let idx = chkArray.controls.findIndex(x => x.value == id);
chkArray.removeAt(idx);
}
}
<div class="col-md-12">
<section class="checkbox-section text-center" *ngIf="notifications && notifications.length > 0">
<label class="example-margin">Notifications to send:</label>
<p *ngFor="let n of notifications; let i = index" formArrayName="notification">
<mat-checkbox class="checkbox-margin" (change)="updateChkbxArray(n.id, $event.checked, 'notification')" value="n.id">{{n.description}}</mat-checkbox>
</p>
</section>
</div>
最後にあなたは保存/更新するためにオリジナルのレコードIDの配列でフォームを保存するようになっています。
改善のために何か発言をさせていただきます。
私の5セントを追加してください)私の質問モデル
{
name: "what_is_it",
options:[
{
label: 'Option name',
value: '1'
},
{
label: 'Option name 2',
value: '2'
}
]
}
template.html
<div class="question" formGroupName="{{ question.name }}">
<div *ngFor="let opt of question.options; index as i" class="question__answer" >
<input
type="checkbox" id="{{question.name}}_{{i}}"
[name]="question.name" class="hidden question__input"
[value]="opt.value"
[formControlName]="opt.label"
>
<label for="{{question.name}}_{{i}}" class="question__label question__label_checkbox">
{{opt.label}}
</label>
</div>
component.ts
onSubmit() {
let formModel = {};
for (let key in this.form.value) {
if (typeof this.form.value[key] !== 'object') {
formModel[key] = this.form.value[key]
} else { //if formgroup item
formModel[key] = '';
for (let k in this.form.value[key]) {
if (this.form.value[key][k])
formModel[key] = formModel[key] + k + ';'; //create string with ';' separators like 'a;b;c'
}
}
}
console.log(formModel)
}