异步管道的Angular 9过滤结果

Posted

技术标签:

【中文标题】异步管道的Angular 9过滤结果【英文标题】:Angular 9 filter results of async pipe 【发布时间】:2020-08-12 11:39:55 【问题描述】:

我是 Angular 方面的新手,我正在做一个应用程序以更好地学习。我对 http 调用的绑定和过滤结果有疑问。在下面的代码中,我尝试绑定一些数据的列表。

在服务中我有这样的电话

 getTestFields(): Observable<Engagement[]> 
   return this.httpClient.get<Engagement[]>(this.url + '/testFields');
 

它基本上返回一些测试字段的列表;在 component.ts 我将调用结果绑定到一个可观察变量。

dataSet$: Observable<Engagement[]>;
ngOnInit(): void 
  this.dataSet$ = this.service.getTestFields();

html 模板中,我将数据绑定如下:

<table class="table table-hover" *ngIf="dataSet$ | async as resultSet; else loading">
        <tr *ngFor="let item of resultSet" >
            <td>item.testName</td>
            <td>item.planned</td>
            <td>item.workingOn</td>
            <td>item.reviewed</td>
        </tr>   </table>

直到这里我都没有问题;我成功获取数据并显示在表格中。问题是过滤;我不知道如何过滤已经从服务器获得的数据。我不想让服务器调用过滤数据,我想过滤我已经得到的当前列表。

我尝试了以下类似的方法,但没有成功。

filter() 
    this.dataSet$ = this.dataSet$.pipe(
    switchMap(data => 
      return data.filter(p => p.planned)
    )
  );
 

如何在不向服务器发送新调用的情况下过滤现有的可观察列表?

【问题讨论】:

【参考方案1】:

尝试将 rxjs map 运算符与数组 filter 结合使用。

Map 转换 observable,因此在本例中,我们使用它来转换数组以仅包含 planned 为 true 的项目。

import  map  from 'rxjs/operators';
.....
ngOnInit(): void 
  this.dataSet$ = this.service.getTestFields().pipe(
    map(data => data.filter(p => p.planned)),
  );

您也可以使用rxjsfilter 运算符从Observable 发射filter,但我认为在这种情况下您不需要它。

================ RxJS过滤器的使用===================

import  filter  from 'rxjs/operators';
...
ngOnInit(): void 
  this.dataSet$ = this.service.getTestFields().pipe(
    // make sure data is truthy and has a length before emitting to 
    // the consumer (in this case it is the HTML).
    filter(data => !!data && !!data.length),
  );

【讨论】:

在哪些情况下这样使用比 rxjs 过滤器更好?有什么特别的区别吗? 是的,filter of array 根据条件过滤数组。 rxjs filter 运算符根据条件停止发射可观察对象并查看您的示例,我认为您不想要这个。我已经编辑了我的答案以包含filter 的示例。【参考方案2】:

我同意 AliF50,只是稍作调整,因为我假设您有一个过滤器按钮,或者您想在加载数据后以某种方式过滤掉。

dataSet$: Observable<Engagement[]>;
ngOnInit(): void 
 filter();


filter() 
    this.dataSet$ = this.service.getTestFields().pipe(
    map(data => data.filter(p => p.planned)),
  );
  );
 

还有一种情况是,一旦加载数据,您就永远不想访问服务器,那么您可能需要在组件级别订阅。

喜欢

dataSet: Engagement[];
ngOnInit(): void 
  this.dataSet = this.service.getTestFields().subscribe(response => this.dataSet = 
   response);


filter() 
    this.dataSet = this.dataSet.filter(data => data.planned === true)
  

【讨论】:

从不访问服务器是什么意思?订阅会产生缓存? 不,它会第一次发出一个请求,并且由于您将保存 this.dataSet 中不可观察的所有值,现在如果您尝试过滤任何内容,它就会打开该对象,因此不需要 api 调用。【参考方案3】:

我遇到了一个混合解决方案;职责分离是解决问题的关键;我在async 管道中实现了一个自动完成功能到它自己的列表,但是我无法正确处理过滤器,所以我们开始吧:

my-dear-autocomplete.component.ts


import 
  ChangeDetectionStrategy,
  Component,
  OnInit,
  Input,
  Output, EventEmitter
 from '@angular/core';
import  FormControl  from '@angular/forms';
import  MyInterface  from '~interfaces-whatsoever';
import  map, startWith  from 'rxjs/operators';
import  Observable  from 'rxjs';


@Component(...)
export class MyDearAutocompleteComponent
  implements OnInit

  @Input() controlName: string;
  @Input() control: FormControl;
  @Input() options: MyInterface[];
  @Output() selectOptionEvent = new EventEmitter<MyInterface>();
  filteredOptions: Observable<MyInterface[]>;

  ngOnInit() 
    this.filteredOptions = this.control.valueChanges.pipe(startWith(''), map(name => this.isOptionNameMatch(name)));
  

  isOptionNameMatch(value: string): MyInterface[] 
    const clause = (option: MyInterface) =>  do your stuff test here ;
    return (typeof value === 'string') ? this.options.filter(clause) : this.options;
  

  selectOption(value: MyInterface) 
    this.selectOptionEvent.emit(value);
  



@Input() options: MyInterface[]; 是组件父级提供的完整列表,使用如下:

options$ | async as options; else loading
...
<app-my-dear-autocomplete ... [options]="options" ... ></app-my-dear-autocomplete>

my-dear-autocomplete.component.html

<mat-form-field appearance="fill" fxFlex="100">
  <mat-label>My dear option list</mat-label>
  <input type="text" required matInput [matAutocomplete]="optionAutocomplete" [formControl]="control"/>
  <mat-autocomplete #optionAutocomplete="matAutocomplete" (optionSelected)="selectOption($event.option.value)">
    <mat-option *ngFor="let option of filteredOptions | async" [value]="option">
       option.whatsoever 
    </mat-option>
  </mat-autocomplete>
</mat-form-field>

这样做并像订阅它输出的组件一样使用自动完成功能有助于我与其他组件一起处理选择事件。

希望它有所帮助,我当然愿意接受建议

【讨论】:

以上是关于异步管道的Angular 9过滤结果的主要内容,如果未能解决你的问题,请参考以下文章

如何在Angular 6中将异步管道的结果分配给没有*ngIf的变量

Angular 5:无法使用异步管道更新模板

Angular/Rxjs 管道异步不适用于 s-s-r?

Angular 5:使用异步管道搜索 - 显示加载指示器

这个 Angular 异步管道的正确含义是啥?

Angular *ngFor 使用异步管道绑定到 observable - 发生了啥?