【问题标题】:How to filter FirebaseListObservable on client side?如何在客户端过滤 FirebaseListObservable?
【发布时间】:2017-01-25 18:13:46
【问题描述】:

我在 Angular Web 应用程序中使用 AngularFire2。由于 Firebase 的查询限制,我无法形成一个准确提供我需要的数据的查询(至少在不对我的架构进行重大更改的情况下不会)。

所以我想在 javascript(打字稿)中应用额外的客户端过滤条件。我该怎么做呢?我可以以某种方式向可观察对象添加过滤器功能吗?下面是一个片段,说明了我在做什么。

在组件的 HTML 模板中,我在下面有类似的内容。 html 片段中的“jobs”变量是 FirebaseListObservable。

<tr *ngFor="let job of jobs | async"> 
  .. fill in the table rows

组件代码如下所示:

   // in the member declaration of the class
   jobs : FirebaseListObservable<Job[]>;

   ...
   // Notice in ngOnInit(), I'm filtering the jobs list using the the
   // Firebase criteria. But I want to filter on an additional field. 
   // Firebase allows only single field criteria, so I'm looking for 
   // a way to apply an additional client-side filter to jobs to eliminate
   // some additional records. 

   ngOnInit(){
      this.jobs = this.af.database.list("/jobs/", {query: {orderByChild : "companyKey", equalTo:someKey}})
   }

有没有办法在“this.jobs”上应用过滤器组件,以便我可以应用本地(客户端)过滤器?

【问题讨论】:

    标签: angular firebase-realtime-database rxjs observable angularfire2


    【解决方案1】:

    我还没有找到真正过滤 observable 的方法,但我找到了某些情况下的解决方法。可以使用 *ngFor 在元素中进行伪过滤。所以我的例子变成了

    <tr *ngFor="let job of jobs | async" [hidden]="filter(job)"> 
      .. fill in the table rows
    

    添加我们将 filter() 方法添加到我们的组件代码中:

    // in the member declaration of the class
       jobs : FirebaseListObservable<Job[]>;
    
       ...
    
       ngOnInit(){
          this.jobs = this.af.database.list("/jobs/", {query: {orderByChild : "companyKey", equalTo:someKey}})
       }
    
       filter(job : Job) : boolean{
         // Return true if don't want this job in the results.
         // e.g. lets filter jobs with price < 25;
         if (job.price < 25){
           return true;
         }
         return false; 
      }
    

    我仍在寻找一种真正过滤 observable 的方法,但与此同时,这种解决方法是可用的。

    【讨论】:

    • 我认为使用 Observables 的原语有更好的方法来做到这一点。有人举个例子吗?
    【解决方案2】:

    我遇到了同样的问题。缺少的部分是过滤数组本身(Job[]),而不是围绕它的 Observable 包装器(FirebaseListObservable&lt;Job[]&gt;)。

    使用 RxJS 运算符 map 能够做到这一点。

    import {Observable} from 'rxjs/Observable';
    import 'rxjs/add/operator/map'
    
    ...
    
    @Component({
      ...
    })
    export class JobComponent {
    
      ...
    
      getJobsLessThanPrice(price: number): Observable<Job[]> {
        return this.af.database.list('jobs', {query: {...}})
          .map(_jobs => _jobs.filter(job => job.price > price));
    
      }
    }
    

    如果 job.price &gt; price 返回 true,则包含在内。

    还注意到您的 jobs 属性是 FirebaseListObservable&lt;Job&gt; 类型,我原以为可观察类型应该是 Job[]Job

    【讨论】:

    • 是的,你是对的。我修正了我的例子。感谢您的出色回答。
    • 我正在尝试应用它而不是管道,但filter 给了我一个类型错误,它不能扣除Job 的类型,它认为unknown。确切的错误Property 'filter' does not exist on type 'unknown'. ts(2339)this.af.database.list 调用的类型是 FirebaseListObservable&lt;Job[]&gt; BTW。
    • 到目前为止,我必须通过.map&lt;Job[], Job[]&gt;(_jobs =&gt; _jobs.filter(job =&gt; job.price &gt; price)) 进行编译,仍然需要测试是否按预期工作。
    • 当我尝试遍历 RxJs observable 时,我得到NgFor only supports binding to Iterables such as Arrays.。我可以遍历FirebaseListObservable
    【解决方案3】:

    您可以编写自己的管道 (类似于 Angular 1.X 上的 Filter

    import {Pipe, PipeTransform} from '@angular/core';
    import * as _ from 'lodash';
    
    @Pipe({
      name: 'search'
    })
    export class SearchPipe implements PipeTransform {
    
     transform(value: any, args?: any): any {
    
      if (args) {
        return _.filter(value, d => _.find(_.valuesIn(d), v => 
            _.toLower(v).indexOf(_.toLower(args)) !== -1));
      }
    
     return value;
     }
     }   
    

    然后,在组件的模板中使用这个管道:

    <input [(ngModel)]="term"> 
    <li *ngFor="let item of items | async | search:term">{{item}}</li>
    

    【讨论】:

      猜你喜欢
      • 2018-04-03
      • 1970-01-01
      • 2010-12-21
      • 2014-09-18
      • 1970-01-01
      • 1970-01-01
      • 2012-12-21
      • 2017-02-20
      • 1970-01-01
      相关资源
      最近更新 更多