Angular: 存储多个复选框值与相关的文本区域值
Angular: store multiple checkbox values with related textarea value
我有一个表单,其中包含多个 checkbox
和相关的 textarea
字段,用于征求意见。请检查演示 here
当用户先选中复选框然后在评论中输入值时,评论值不会被存储。如果用户先输入评论值,然后选中复选框,然后添加值。
textarea
值为 optional
。如果用户选中复选框,则应添加评论。如果选中复选框然后输入评论,则应添加评论。如果先添加评论然后选中复选框,则也应该添加评论。如果取消选中该复选框,则应将其删除。行为应该与 select/unselect 所有选项相同。
我应该怎么做?
Html
<div style="text-align:center">
<h1>
{{ title }}
</h1>
</div>
<div class="container">
<div class="text-center mt-5">
<div class="row">
<div class="col-md-6">
<ul class="list-group">
<li class="list-group-item">
<input
type="checkbox"
[(ngModel)]="masterSelected"
name="list_name"
value="m1"
(change)="checkUncheckAll()"
/>
<strong>Select/ Unselect All</strong>
</li>
</ul>
<ul class="list-group">
<li class="list-group-item" *ngFor="let item of checklist">
<input
type="checkbox"
[(ngModel)]="item.isSelected"
name="list_name"
value="{{ item.id }}"
(change)="isAllSelected()"
/>
<textarea [(ngModel)]="item.comment">{{ item.comment }}</textarea>
{{ item.value }}
</li>
</ul>
</div>
<div class="col-md-6">
<code>{{ checkedList }}</code>
</div>
</div>
</div>
</div>
TS
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
name = 'Angular';
masterSelected: boolean;
checklist: any;
checkedList: any;
constructor() {
this.masterSelected = false;
this.checklist = [
{ id: 1, value: 'Elenor Anderson', comment: '', isSelected: false },
{ id: 2, value: 'Caden Kunze', comment: 'test', isSelected: true },
{ id: 3, value: 'Ms. Hortense Zulauf', comment: '123', isSelected: true },
{ id: 4, value: 'Grady Reichert', comment: '', isSelected: false },
{ id: 5, value: 'Dejon Olson', comment: '', isSelected: false },
{ id: 6, value: 'Jamir Pfannerstill', comment: '', isSelected: false },
{ id: 7, value: 'Aracely Renner DVM', comment: '', isSelected: false },
{ id: 8, value: 'Genoveva Luettgen', comment: '', isSelected: false },
];
this.getCheckedItemList();
}
// The master checkbox will check/ uncheck all items
checkUncheckAll() {
for (var i = 0; i < this.checklist.length; i++) {
this.checklist[i].isSelected = this.masterSelected;
}
this.getCheckedItemList();
}
// Check All Checkbox Checked
isAllSelected() {
this.masterSelected = this.checklist.every(function (item: any) {
return item.isSelected == true;
});
this.getCheckedItemList();
}
// Get List of Checked Items
getCheckedItemList() {
this.checkedList = [];
for (var i = 0; i < this.checklist.length; i++) {
if (this.checklist[i].isSelected)
this.checkedList.push(this.checklist[i]);
}
this.checkedList = JSON.stringify(this.checkedList);
}
}
请多多指教
因为我有一些时间,我想我会为你写一个可能的解决方案......在评论中提到,我会建议一个表单,因为如果 textarea 有价值,你需要复选框。这是一个被动的方法。
我已经创建了一个表单组,你不一定需要它,你也可以只使用一个表单数组,而不需要父表单。
无论如何,在这种情况下,我声明一个带有 formarray 的表单组,我们将在其中存储我们的值,然后我们根据您的数组填充 formarray。我们监听每个文本区域的变化,如果它有值,我们设置 Validators.requiredTrue
(requiredTrue
用于复选框),否则我们用 clearValidators
清除它。最后我们用 updateValueAndValidity
.
更新表单控件的值和有效性
我们可以使用单独的表单控件来选中/取消选中所有复选框,我在这里将其命名为 masterSwitch
;)
我们监听该字段的变化并更新表单数组中的值,如果值为 true
.
则清除任何可能的 requiredTrue
验证器
所以上面的解释转化为下面的TS代码:
myForm!: FormGroup;
alive = true;
// our unselect / select all field
masterSwitch = new FormControl(false);
checklist = [....];
constructor(private fb: FormBuilder) {
this.myForm = this.fb.group({
checklist: this.fb.array([]),
});
// add values to formarray
this.populate();
// listen to all comment formcontrols and add / remove required validator
merge(
...this.formChecklist.controls.map(
(control: AbstractControl, index: number) =>
control.get('comment').valueChanges.pipe(
tap((value) => {
const isSelected = control.get('isSelected');
if (value.trim()) {
isSelected.setValidators(Validators.requiredTrue);
} else {
isSelected.clearValidators();
}
isSelected.updateValueAndValidity();
})
)
)
)
.pipe(takeWhile((_) => this.alive))
.subscribe();
// listen to the select / unselect all and toggle checkbox as well as handle validators
this.masterSwitch.valueChanges
.pipe(takeWhile((_) => this.alive))
.subscribe((value: boolean) => {
this.formChecklist.controls.forEach((ctrl: FormGroup) => {
ctrl.patchValue({ isSelected: value });
const isSelected = ctrl.get('isSelected');
if (value) {
isSelected.clearValidators();
} else {
if (ctrl.get('comment').value) {
isSelected.addValidators(Validators.requiredTrue);
isSelected.updateValueAndValidity();
}
}
});
});
}
get formChecklist() {
return this.myForm.get('checklist') as FormArray;
}
populate() {
this.checklist.forEach((x) => {
this.formChecklist.push(
this.fb.group({
id: x.id,
value: x.value,
comment: x.comment,
isSelected: x.isSelected,
})
);
});
}
ngOnDestroy() {
this.alive = false;
}
在模板中,我们只是做通常的响应式表单,通过 formarray 循环并显示表单控件,没有什么特别的:
<label>
<input type="checkbox" [formControl]="masterSwitch" />
Uncheck / Check all
</label>
<hr>
<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
<div formArrayName="checklist">
<div *ngFor="let item of formChecklist.controls; let i = index">
<div [formGroupName]="i">
<label>
<input type="checkbox" formControlName="isSelected" />
{{ item.get('value').value }}
</label>
<textarea formControlName="comment"></textarea>
<small *ngIf="item.get('isSelected').hasError('required')"
>Checkbox Required!</small
>
</div>
</div>
</div>
<button>Submit</button>
</form>
并且在提交表单时,您可以过滤掉未选中的表单数组中的对象,我没有在此处包含它,因为它是基本的 js filter
:)
最后...一个STACKBLITZ供大家参考
双向绑定工作正常,注释更改正确存储在您在 属性 checklist
中的对象中
您看不到应用程序中打印的更改,因为在过滤后的 属性 checkedList
中,您正在 字符串化 过滤后的数组(我猜测打印它)。
如果您在方法 getCheckedItemList()
中删除行 this.checkedList = JSON.stringify(this.checkedList);
并使用 json 管道 <code>{{ checkedList | json }}</code>
在模板中打印 属性,您会看到更改已正确存储。
干杯
只需将 (input)="getCheckedItemList()"
添加到您的 textarea
。
我有一个表单,其中包含多个 checkbox
和相关的 textarea
字段,用于征求意见。请检查演示 here
当用户先选中复选框然后在评论中输入值时,评论值不会被存储。如果用户先输入评论值,然后选中复选框,然后添加值。
textarea
值为 optional
。如果用户选中复选框,则应添加评论。如果选中复选框然后输入评论,则应添加评论。如果先添加评论然后选中复选框,则也应该添加评论。如果取消选中该复选框,则应将其删除。行为应该与 select/unselect 所有选项相同。
我应该怎么做?
Html
<div style="text-align:center">
<h1>
{{ title }}
</h1>
</div>
<div class="container">
<div class="text-center mt-5">
<div class="row">
<div class="col-md-6">
<ul class="list-group">
<li class="list-group-item">
<input
type="checkbox"
[(ngModel)]="masterSelected"
name="list_name"
value="m1"
(change)="checkUncheckAll()"
/>
<strong>Select/ Unselect All</strong>
</li>
</ul>
<ul class="list-group">
<li class="list-group-item" *ngFor="let item of checklist">
<input
type="checkbox"
[(ngModel)]="item.isSelected"
name="list_name"
value="{{ item.id }}"
(change)="isAllSelected()"
/>
<textarea [(ngModel)]="item.comment">{{ item.comment }}</textarea>
{{ item.value }}
</li>
</ul>
</div>
<div class="col-md-6">
<code>{{ checkedList }}</code>
</div>
</div>
</div>
</div>
TS
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
name = 'Angular';
masterSelected: boolean;
checklist: any;
checkedList: any;
constructor() {
this.masterSelected = false;
this.checklist = [
{ id: 1, value: 'Elenor Anderson', comment: '', isSelected: false },
{ id: 2, value: 'Caden Kunze', comment: 'test', isSelected: true },
{ id: 3, value: 'Ms. Hortense Zulauf', comment: '123', isSelected: true },
{ id: 4, value: 'Grady Reichert', comment: '', isSelected: false },
{ id: 5, value: 'Dejon Olson', comment: '', isSelected: false },
{ id: 6, value: 'Jamir Pfannerstill', comment: '', isSelected: false },
{ id: 7, value: 'Aracely Renner DVM', comment: '', isSelected: false },
{ id: 8, value: 'Genoveva Luettgen', comment: '', isSelected: false },
];
this.getCheckedItemList();
}
// The master checkbox will check/ uncheck all items
checkUncheckAll() {
for (var i = 0; i < this.checklist.length; i++) {
this.checklist[i].isSelected = this.masterSelected;
}
this.getCheckedItemList();
}
// Check All Checkbox Checked
isAllSelected() {
this.masterSelected = this.checklist.every(function (item: any) {
return item.isSelected == true;
});
this.getCheckedItemList();
}
// Get List of Checked Items
getCheckedItemList() {
this.checkedList = [];
for (var i = 0; i < this.checklist.length; i++) {
if (this.checklist[i].isSelected)
this.checkedList.push(this.checklist[i]);
}
this.checkedList = JSON.stringify(this.checkedList);
}
}
请多多指教
因为我有一些时间,我想我会为你写一个可能的解决方案......在评论中提到,我会建议一个表单,因为如果 textarea 有价值,你需要复选框。这是一个被动的方法。
我已经创建了一个表单组,你不一定需要它,你也可以只使用一个表单数组,而不需要父表单。
无论如何,在这种情况下,我声明一个带有 formarray 的表单组,我们将在其中存储我们的值,然后我们根据您的数组填充 formarray。我们监听每个文本区域的变化,如果它有值,我们设置 Validators.requiredTrue
(requiredTrue
用于复选框),否则我们用 clearValidators
清除它。最后我们用 updateValueAndValidity
.
我们可以使用单独的表单控件来选中/取消选中所有复选框,我在这里将其命名为 masterSwitch
;)
我们监听该字段的变化并更新表单数组中的值,如果值为 true
.
requiredTrue
验证器
所以上面的解释转化为下面的TS代码:
myForm!: FormGroup;
alive = true;
// our unselect / select all field
masterSwitch = new FormControl(false);
checklist = [....];
constructor(private fb: FormBuilder) {
this.myForm = this.fb.group({
checklist: this.fb.array([]),
});
// add values to formarray
this.populate();
// listen to all comment formcontrols and add / remove required validator
merge(
...this.formChecklist.controls.map(
(control: AbstractControl, index: number) =>
control.get('comment').valueChanges.pipe(
tap((value) => {
const isSelected = control.get('isSelected');
if (value.trim()) {
isSelected.setValidators(Validators.requiredTrue);
} else {
isSelected.clearValidators();
}
isSelected.updateValueAndValidity();
})
)
)
)
.pipe(takeWhile((_) => this.alive))
.subscribe();
// listen to the select / unselect all and toggle checkbox as well as handle validators
this.masterSwitch.valueChanges
.pipe(takeWhile((_) => this.alive))
.subscribe((value: boolean) => {
this.formChecklist.controls.forEach((ctrl: FormGroup) => {
ctrl.patchValue({ isSelected: value });
const isSelected = ctrl.get('isSelected');
if (value) {
isSelected.clearValidators();
} else {
if (ctrl.get('comment').value) {
isSelected.addValidators(Validators.requiredTrue);
isSelected.updateValueAndValidity();
}
}
});
});
}
get formChecklist() {
return this.myForm.get('checklist') as FormArray;
}
populate() {
this.checklist.forEach((x) => {
this.formChecklist.push(
this.fb.group({
id: x.id,
value: x.value,
comment: x.comment,
isSelected: x.isSelected,
})
);
});
}
ngOnDestroy() {
this.alive = false;
}
在模板中,我们只是做通常的响应式表单,通过 formarray 循环并显示表单控件,没有什么特别的:
<label>
<input type="checkbox" [formControl]="masterSwitch" />
Uncheck / Check all
</label>
<hr>
<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
<div formArrayName="checklist">
<div *ngFor="let item of formChecklist.controls; let i = index">
<div [formGroupName]="i">
<label>
<input type="checkbox" formControlName="isSelected" />
{{ item.get('value').value }}
</label>
<textarea formControlName="comment"></textarea>
<small *ngIf="item.get('isSelected').hasError('required')"
>Checkbox Required!</small
>
</div>
</div>
</div>
<button>Submit</button>
</form>
并且在提交表单时,您可以过滤掉未选中的表单数组中的对象,我没有在此处包含它,因为它是基本的 js filter
:)
最后...一个STACKBLITZ供大家参考
双向绑定工作正常,注释更改正确存储在您在 属性 checklist
您看不到应用程序中打印的更改,因为在过滤后的 属性 checkedList
中,您正在 字符串化 过滤后的数组(我猜测打印它)。
如果您在方法 getCheckedItemList()
中删除行 this.checkedList = JSON.stringify(this.checkedList);
并使用 json 管道 <code>{{ checkedList | json }}</code>
在模板中打印 属性,您会看到更改已正确存储。
干杯
只需将 (input)="getCheckedItemList()"
添加到您的 textarea
。