【问题标题】:How to filter API response with Angular 2 HTTP?如何使用 Angular 2 HTTP 过滤 API 响应?
【发布时间】:2017-09-03 19:51:40
【问题描述】:

这是我的交易 API 响应:

[{
   "id": 1,
   "description": "Sandwich",
   "date": "2017-09-01",
   "category": "Take away",
   "tags": ["Holidays"],
   "amount": -2
},{
   "id": 2,
   "description": "Wage",
   "date": "2017-08-31",
   "category": "Wage",
   "tags": [],
   "amount": 2000
}]

我的 Angular 服务中有以下代码:

@Injectable()
export class TransactionsService {
    private _transactionssurl = 'app/transactions/transactions.json';

    constructor(private _http: Http){}

    getTransactions(query = {}): Observable<ITransaction[]> {
        return this._http.get(this._transactionssurl)
            .map((response: Response) => <ITransaction[]> response.json())
            .filter((value: any) => {
                console.log(value)
                if (query["month"] && transaction["month"].indexOf(query["month"]) === 0 ) return false;
                if (query["category"] && transaction["category"] !== query["category"]) return false;
                if (query["tag"] && transaction["tags"].includes(query["tag"])) return false;
                return true;
            })
            .do(data => console.log(data))
            .catch(this.handleError);
    }

    //...

}

您可以在上面看到我正在尝试根据传入的查询对象过滤数据,例如{month: "2017-09", tag: "Holidays"} 但是,似乎传递给过滤器函数的函数参数-事务-是整个事务数组,而不仅仅是我期望的单个事务对象-我应该如何安排代码进行过滤每个单独?我应该在这里做什么?我看到的大多数示例看起来有点像这样,所以我尝试复制它们。

【问题讨论】:

  • 您正在调用 Observable.filter,它会过滤事件流,不是 Array.filter
  • 您正在尝试过滤可观察对象(即可观察对象发出的事件),而不是过滤可观察对象发出的数组元素。从响应中提取数组后,过滤应该在内部传递给 map() 的回调函数中。

标签: angular angular2-http


【解决方案1】:

操作员操作的可观察流是整个响应,在这种情况下是数据列表,而不是该列表中的元素。您只需要根据您想要的结果采取相应的行动......如果您希望列表中的每个项目都转换为可观察流中的项目,因此它们会通过运算符一一通过最终消费者,那么您可以这样做:

getTransactions(query = {}): Observable<ITransaction[]> {
    return this._http.get(this._transactionssurl)
        .map((response: Response) => <ITransaction[]> response.json())
        .flatMap(data => Observable.from(data))
        .filter((value: any) => {
            console.log(value)
            if (query["month"] && transaction["month"].indexOf(query["month"]) === 0 ) return false;
            if (query["category"] && transaction["category"] !== query["category"]) return false;
            if (query["tag"] && transaction["tags"].includes(query["tag"])) return false;
            return true;
        })
        .do(data => console.log(data))
        .catch(this.handleError);
}

flatMap -> from 操作获取您的列表数据并将其展平为可观察的流。但是,如果您实际上希望最终消费者的整个列表在一个可观察的流项目中,这是没有意义的,因为您必须在最后使用 reduce 运算符重新聚合响应,例如:

 .reduce((acc, val) => acc.concat([val]), [])

如果您只是想过滤列表并传递它,这很简单:

getTransactions(query = {}): Observable<ITransaction[]> {
    return this._http.get(this._transactionssurl)
        .map((response: Response) => <ITransaction[]> response.json())
        .map((list: any) => {
            return list.filter(value => {
                if (query["month"] && transaction["month"].indexOf(query["month"]) === 0 ) return false;
                if (query["category"] && transaction["category"] !== query["category"]) return false;
                if (query["tag"] && transaction["tags"].includes(query["tag"])) return false;
                return true;
            });
        })
        .do(data => console.log(data))
        .catch(this.handleError);
}

这只是期望流中的数据数组并在其上运行普通数组过滤器方法并将其映射到过滤列表中。

【讨论】:

  • 感谢您非常详细的回复。我从 map 调用内部尝试了另一种方法,而不是将 filter() 链接到那个,所以我猜 response.json().filter(...) 是相似的?我看到你已经链接了另一个 map() 调用,虽然看起来更整洁。
  • 我的偏好是让每个操作员尽可能少地单独操作。我认为当你几个月后回来并忘记你为什么做你所做的事情时,代码更干净,更容易理解和维护
猜你喜欢
  • 2019-02-03
  • 1970-01-01
  • 2016-03-13
  • 2012-08-31
  • 2017-09-09
  • 2020-08-09
  • 1970-01-01
  • 2022-07-06
  • 2020-08-14
相关资源
最近更新 更多