如何 select Angular 2 中 ngFor 内的所有筛选复选框?
How to select all filtered checkboxes inside ngFor in Angular 2?
我有一个 Angular 2 组件,它在 *ngFor
内加载并向用户显示复选框列表,该列表也可以根据键进行过滤。我需要 select 所有过滤的项目并将它们添加到数组中。我可以选中所有复选框,但问题是当我有问题地更改 checked
时 change
事件不会触发,知道如何解决这个问题吗?
模板:
<div class="form-group">
<input type="text" class="form-control" id="stringKeyFilter" placeholder="Key Filter"
[(ngModel)]="keyFilter">
</div>
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Id</th>
<th style="width: 150px;">Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let device of devices| stringFilter: keyFilter">
<td>
{{device.$key}}
</td>
<td>
<div class="checkbox">
<label> <input type="checkbox"
[checked]="selectAll || selectedDevices.indexOf(device.$key) > -1"
(change)="updateSelectedDevices(device.$key, $event)" > View</label>
</div>
</td>
</tr>
</tbody>
</table>
<div class="btn-group pull-right">
<button type="button" class="btn btn-primary" (click)="selectAllDevices()">Select All</button>
<button type="button" class="btn btn-default" (click)="deselectAllDevices()">Deselect All
</button>
</div>
组件:
@Component({
moduleId: module.id,
selector: 'device-list',
template: template
})
export class DeviceListComponent implements OnInit {
devicesObservable: FirebaseListObservable<Device[]>;
devices: Device[] = [];
isLoading: boolean = true;
selectedDevices: string[] = [];
selectAll: boolean = false;
allCtrl: any;
keyFilter: string = '';
constructor(private af: AngularFire) {
}
ngOnInit(): void {
this.devicesObservable = this.af.database.list('/online', {
query: {
orderByKey: true,
}
});
this.devicesObservable.subscribe((devicesData)=> {
this.devices = devicesData;
this.isLoading = false
});
}
updateSelectedDevices(deviceId: string, event): void {
if (event.target.checked) {
this.selectedDevices.push(deviceId);
}
else {
_.pull(this.selectedDevices, deviceId);
}
}
loadingDeviceDetail(loading: boolean): void {
this.isLoading = loading;
}
selectAllDevices(): void {
this.selectAll = true;
}
deselectAllDevices(): void {
this.selectAll = false;
this.selectedDevices = [];
}
}
当您尝试 select 所有设备时,您的主列表没有更新,没有任何变化并且 angular 没有检测到它的变化。 (有一个更新的标志,但列表是相同的)。您有两个选择:
- 在列表中的每个选中元素上放置一个标志。
- 触发手动渲染
ApplicationRef.tick()
https://angular.io/docs/ts/latest/api/core/index/ApplicationRef-class.html#!#tick-anchor
更新
更新了示例代码和 plunker 以演示 select/deselect 筛选列表的所有复选框。
根据我的理解,过滤器行为从 Angular1 更改为 Angular2。
在 Angular1 中,当修改数据或过滤器参数时,DOM(网页)将重新计算。 (很久以前我可能错了:P)
在Angular2中,简而言之,修改数据(数组)不会触发过滤器计算,所以没有新结果,没有更新DOM。
官方的长篇解释在这里:Angular2 Pipes - ts。如果你坚持在模板中使用 pipe(filter),你应该探索文档中的 impure pipe 部分。
另一种解决方案是不对 *ngFor
使用过滤器。改为创建过滤列表。
笨蛋
http://plnkr.co/edit/pEpaj0?p=preview
示例代码
app.component.ts
import {Component} from '@angular/core';
import {bootstrap} from '@angular/platform-browser-dynamic';
@Component({
selector: 'material-app',
templateUrl: 'app.component.html'
})
export class AppComponent {
title='How to select all filtered checkboxes inside ngFor in Angular 2?';
url='
device = [
{'name':'abc','checked':false},
{'name':'abcd','checked':false},
{'name':'abcde','checked':false},
{'name':'abc123','checked':false},
{'name':'abc1234','checked':false}];
deviceFilter = '';
deviceFiltered = this.device.slice(0);
selectFiltered(){
this.deviceFiltered.forEach(i=>i.checked=true);
}
deSelectFiltered(){
this.deviceFiltered.forEach(i=>i.checked=false);
}
onFilterChange(){
if (this.deviceFilter.length > 0) {
this.deviceFiltered = this.device.filter(i => i.name.indexOf(this.deviceFilter) > -1);
console.log(this.deviceFiltered);
} else {
this.deviceFiltered = this.device.slice(0);
}
}
}
app.component.html
<h2><a [href]="titleUrl">{{title}}</a></h2>
<div>
Filter
<input [(ngModel)]="deviceFilter" (ngModelChange)="onFilterChange()">
</div>
<div>
List:
<ul>
<li *ngFor="let i of device">
<input type="checkbox" [(ngModel)]="i.checked">{{i.name}}
</li>
</ul>
</div>
<div>
Filtered List:
<ul>
<li *ngFor="let i of deviceFiltered">
<input type="checkbox" [(ngModel)]="i.checked">{{i.name}}
</li>
</ul>
<button (click)="selectFiltered()">Select Filtered</button>
<button (click)="deSelectFiltered()">Deselect Filtered</button>
</div>
注意:您的代码可能会有所不同,因为我进行了一些更改以仅获得可运行的应用程序。请从代码片段中获取相关部分并申请。
这是您问题的工作代码。
以下是您需要对代码进行的更改。
<input type="checkbox" [checked]="selectedDevices.indexOf(device.$key) > -1"
(change)="updateSelectedDevices(device.$key, $event)" >
将您的 select 所有函数更改为以下内容。
selectAllDevices(): void {
this.selectedDevices = this.strFilter(this.devices, this.keyFilter).map(dev=>dev.$key);
}
注:this.strFilter
是组件class/function中的过滤函数。
这取决于您的过滤器 class。我已经像这样创建了 filter/pipe。
//stringfilter.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({name: 'stringFilter'})
export class StringFilter implements PipeTransform {
transform(value: Array<any>, filterKey: string): Array<any> {
return (
filterKey ? value.
filter(el => (el.name).toLowerCase().includes(filterKey))
: value
);
}
}
在组件 class 中我确实喜欢
//组件class
import {StringFilter} from './filter-file'
和
ngOnInit(): void {
this.strFilter = new StringFilter().transform;
//this.devices = [];
this.isLoading = false;
}
我测试了以下代码。希望这能解决您的问题。
我有一个 Angular 2 组件,它在 *ngFor
内加载并向用户显示复选框列表,该列表也可以根据键进行过滤。我需要 select 所有过滤的项目并将它们添加到数组中。我可以选中所有复选框,但问题是当我有问题地更改 checked
时 change
事件不会触发,知道如何解决这个问题吗?
模板:
<div class="form-group">
<input type="text" class="form-control" id="stringKeyFilter" placeholder="Key Filter"
[(ngModel)]="keyFilter">
</div>
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Id</th>
<th style="width: 150px;">Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let device of devices| stringFilter: keyFilter">
<td>
{{device.$key}}
</td>
<td>
<div class="checkbox">
<label> <input type="checkbox"
[checked]="selectAll || selectedDevices.indexOf(device.$key) > -1"
(change)="updateSelectedDevices(device.$key, $event)" > View</label>
</div>
</td>
</tr>
</tbody>
</table>
<div class="btn-group pull-right">
<button type="button" class="btn btn-primary" (click)="selectAllDevices()">Select All</button>
<button type="button" class="btn btn-default" (click)="deselectAllDevices()">Deselect All
</button>
</div>
组件:
@Component({
moduleId: module.id,
selector: 'device-list',
template: template
})
export class DeviceListComponent implements OnInit {
devicesObservable: FirebaseListObservable<Device[]>;
devices: Device[] = [];
isLoading: boolean = true;
selectedDevices: string[] = [];
selectAll: boolean = false;
allCtrl: any;
keyFilter: string = '';
constructor(private af: AngularFire) {
}
ngOnInit(): void {
this.devicesObservable = this.af.database.list('/online', {
query: {
orderByKey: true,
}
});
this.devicesObservable.subscribe((devicesData)=> {
this.devices = devicesData;
this.isLoading = false
});
}
updateSelectedDevices(deviceId: string, event): void {
if (event.target.checked) {
this.selectedDevices.push(deviceId);
}
else {
_.pull(this.selectedDevices, deviceId);
}
}
loadingDeviceDetail(loading: boolean): void {
this.isLoading = loading;
}
selectAllDevices(): void {
this.selectAll = true;
}
deselectAllDevices(): void {
this.selectAll = false;
this.selectedDevices = [];
}
}
当您尝试 select 所有设备时,您的主列表没有更新,没有任何变化并且 angular 没有检测到它的变化。 (有一个更新的标志,但列表是相同的)。您有两个选择:
- 在列表中的每个选中元素上放置一个标志。
- 触发手动渲染 ApplicationRef.tick() https://angular.io/docs/ts/latest/api/core/index/ApplicationRef-class.html#!#tick-anchor
更新
更新了示例代码和 plunker 以演示 select/deselect 筛选列表的所有复选框。
根据我的理解,过滤器行为从 Angular1 更改为 Angular2。
在 Angular1 中,当修改数据或过滤器参数时,DOM(网页)将重新计算。 (很久以前我可能错了:P)
在Angular2中,简而言之,修改数据(数组)不会触发过滤器计算,所以没有新结果,没有更新DOM。
官方的长篇解释在这里:Angular2 Pipes - ts。如果你坚持在模板中使用 pipe(filter),你应该探索文档中的 impure pipe 部分。
另一种解决方案是不对 *ngFor
使用过滤器。改为创建过滤列表。
笨蛋
http://plnkr.co/edit/pEpaj0?p=preview
示例代码
app.component.ts
import {Component} from '@angular/core';
import {bootstrap} from '@angular/platform-browser-dynamic';
@Component({
selector: 'material-app',
templateUrl: 'app.component.html'
})
export class AppComponent {
title='How to select all filtered checkboxes inside ngFor in Angular 2?';
url='
device = [
{'name':'abc','checked':false},
{'name':'abcd','checked':false},
{'name':'abcde','checked':false},
{'name':'abc123','checked':false},
{'name':'abc1234','checked':false}];
deviceFilter = '';
deviceFiltered = this.device.slice(0);
selectFiltered(){
this.deviceFiltered.forEach(i=>i.checked=true);
}
deSelectFiltered(){
this.deviceFiltered.forEach(i=>i.checked=false);
}
onFilterChange(){
if (this.deviceFilter.length > 0) {
this.deviceFiltered = this.device.filter(i => i.name.indexOf(this.deviceFilter) > -1);
console.log(this.deviceFiltered);
} else {
this.deviceFiltered = this.device.slice(0);
}
}
}
app.component.html
<h2><a [href]="titleUrl">{{title}}</a></h2>
<div>
Filter
<input [(ngModel)]="deviceFilter" (ngModelChange)="onFilterChange()">
</div>
<div>
List:
<ul>
<li *ngFor="let i of device">
<input type="checkbox" [(ngModel)]="i.checked">{{i.name}}
</li>
</ul>
</div>
<div>
Filtered List:
<ul>
<li *ngFor="let i of deviceFiltered">
<input type="checkbox" [(ngModel)]="i.checked">{{i.name}}
</li>
</ul>
<button (click)="selectFiltered()">Select Filtered</button>
<button (click)="deSelectFiltered()">Deselect Filtered</button>
</div>
注意:您的代码可能会有所不同,因为我进行了一些更改以仅获得可运行的应用程序。请从代码片段中获取相关部分并申请。
这是您问题的工作代码。
以下是您需要对代码进行的更改。
<input type="checkbox" [checked]="selectedDevices.indexOf(device.$key) > -1"
(change)="updateSelectedDevices(device.$key, $event)" >
将您的 select 所有函数更改为以下内容。
selectAllDevices(): void {
this.selectedDevices = this.strFilter(this.devices, this.keyFilter).map(dev=>dev.$key);
}
注:this.strFilter
是组件class/function中的过滤函数。
这取决于您的过滤器 class。我已经像这样创建了 filter/pipe。
//stringfilter.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({name: 'stringFilter'})
export class StringFilter implements PipeTransform {
transform(value: Array<any>, filterKey: string): Array<any> {
return (
filterKey ? value.
filter(el => (el.name).toLowerCase().includes(filterKey))
: value
);
}
}
在组件 class 中我确实喜欢 //组件class
import {StringFilter} from './filter-file'
和
ngOnInit(): void {
this.strFilter = new StringFilter().transform;
//this.devices = [];
this.isLoading = false;
}
我测试了以下代码。希望这能解决您的问题。