Angular 7 双向绑定:以数组作为参数的管道无法按预期工作

Angular 7 two-way binding: pipe taking an array as argument does not work as expected

我写了一个 Angular 7 管道,它有两个参数:一个要过滤的数组和一个匹配过滤器的数组。

import { Pipe, PipeTransform } from '@angular/core';

import { Order } from '../models';

@Pipe({
  name: 'filterOrdersByDate'
})
export class CapacityOrderPipe implements PipeTransform {

  transform(orders: Order[], periods: string[]): Order[] {

    if (!orders) return [];
    if (!periods.length) return orders;

    return orders.filter(function(order: Order) {

      for (var i=0; i<periods.length; i++) if (order.crossId.indexOf(periods[i])>-1) return true;
      return false;

    });

  }

}

orders 是一个订单数组,如 Order 模型中所定义。每个订单都有一个crossId 属性。这是表示开始日期的字符串数组。

在我的 component.ts 中,我使用此方法 add/remove 来自周期数组的日期:

toggleOrder(date: string) {
  let i = this.filters.orders.indexOf(date);
  (i<0)? this.filters.orders.push(date) : this.filters.orders.splice(i, 1);
}

这是涉及的component.html代码:

<mat-list class="list">
  <mat-list-item *ngFor="let o of (data.orders | filterOrdersByDate : filters.orders)">
    <mat-divider></mat-divider>
    <h3 mat-line>Order: {{o.group}}-{{o.number}}</h3>
    ...
   </mat-list-item>
</mat-list>

...

<div *ngFor="let f of filters">
<mat-checkbox color="primary" [indeterminate]="false" (change)="toggleOrder(f.date)">{{f.date}}</mat-checkbox>
</div>

当我切换 component.html 中的复选框时,toggleOrder() 方法在过滤器数组中添加或删除日期。所以,我希望管道会过滤 data.orders 数组。不幸的是,这不起作用。我花了 2 个小时寻找解决方案,我发现,如果我将过滤器数组长度传递给管道,一切都会按预期进行。因此,似乎 Angular 将双向绑定设置为过滤器数组长度,而不是过滤器数组本身。

这是如何运作的?我希望 Angular 为组件 class 中定义的每个属性设置双向绑定:字符串、数组、数字等。但是管道不监听数组内容的变化。只有当我将数组长度传递给管道时,它才有效。但是,如果我有一个使用字符串而不是数组来过滤数据的管道,它就可以完美地工作。

如果您查看 Angular docs it talks about scenarios such as these. It discourages 管道由于性能不佳而以这种方式在数组上运行。

您正在尝试通过以下两种方式之一实现的目标。

您可以将管道更新为 'impure' 管道:

@Pipe({
  name: 'filterOrdersByDate',
  pure: false
})

或者您可以将其保留为纯管道并允许 Angular 通过每次重新分配数组值来利用内置的更改检测来获取您的更改。

toggleOrder(date: string) {
  let i = this.filters.orders.indexOf(date);
  (i<0)? this.filters.orders.push(date) : this.filters.orders.splice(i, 1);
  this.filters.orders = this.filters.orders.slice(0);
}

正如最初所述,这不是执行此任务的最佳方式。我会坚持 Angular 的建议,并将此管道移至可以从您的 toggleOrder 方法调用的方法。

The Angular team and many experienced Angular developers strongly recommend moving filtering and sorting logic into the component itself. The component can expose a filteredHeroes or sortedHeroes property and take control over when and how often to execute the supporting logic. Any capabilities that you would have put in a pipe and shared across the app can be written in a filtering/sorting service and injected into the component.