2017-09-06 46 views
0

我想在Angular/TypeScript中合并2个HTTP获取搜索,但我无法正常工作。如何在Angular Heroes教程中合并2个搜索?

使用Angular 2/4 Tour of Heroes教程来说明情况,该教程使用Observable通过“name”值搜索英雄(https://angular.io/tutorial/toh-pt6#add-the-ability-to-search-by-name)。在英雄search.service.ts的代码如下:

import { Injectable } from '@angular/core'; 
import { Http }  from '@angular/http'; 

import { Observable }  from 'rxjs/Observable'; 
import 'rxjs/add/operator/map'; 

import { Hero }   from './hero'; 

@Injectable() 
export class HeroSearchService { 

    constructor(private http: Http) {} 

    search(term: string): Observable<Hero[]> { 
    return this.http 
       .get(`api/heroes/?name=${term}`) 
       .map(response => response.json().data as Hero[]); 
    } 
} 

我需要做添加第二个字符串值“代名词”的英雄类相当于:

export class Hero { 
    id: number; 
    name: string; 
    synonym: string; 
} 

和代

import { InMemoryDbService } from 'angular-in-memory-web-api'; 
export class InMemoryDataService implements InMemoryDbService { 
    createDb() { 
    const heroes = [ 
     { id: 0, name: 'Zero', synonym: 'little' }, 
     { id: 11, name: 'Mr. Nice', synonym: 'pleasant' }, 
     { id: 12, name: 'Narco', synonym: 'sleep' }, 
     { id: 13, name: 'Bombasto', synonym: 'loud' }, 
     { id: 14, name: 'Celeritas', synonym: 'vegetable' }, 
     { id: 15, name: 'Magneta', synonym: 'color' }, 
     { id: 16, name: 'RubberMan', synonym: 'hose' }, 
     { id: 17, name: 'Dynama', synonym: 'motor' }, 
     { id: 18, name: 'Dr IQ', synonym: 'smart' }, 
     { id: 19, name: 'Magma', synonym: 'volcanic' }, 
     { id: 20, name: 'Tornado', synonym: 'wind' } 
    ]; 
    return {heroes}; 
    } 
} 

我可以在英雄search.service.ts的同义词,然后搜索:添加一个同义词每个英雄

 .get(`api/heroes/?synonym=${term}`) 

我的问题是如何搜索名称和同义词,并返回匹配名称或同义词。

从ReactiveX文档看来,答案似乎是使用合并运算符:http://reactivex.io/documentation/operators/merge.html。但是,我无法使其工作。我试着使用合并,包括上面的英雄search.service.ts代码以下新版本的各种方式:

import { Injectable } from '@angular/core'; 
import { Http }  from '@angular/http'; 

import { Observable }  from 'rxjs/Observable'; 
import 'rxjs/add/operator/map'; 
import 'rxjs/add/operator/merge'; 

import { Hero }   from './hero'; 

@Injectable() 
export class HeroSearchService { 

    constructor(private http: Http) {} 

    search(term: string): Observable<Hero[]> { 
    const nameResults = this.http.get(`api/heroes/?name=${term}`); 
    const synonymResults = this.http.get(`api/heroes/?synonym=${term}`);  
    nameResults.merge(synonymResults); 
    return nameResults.map(response => response.json().data as Hero[]); 
    } 
} 

的未工作,妥善代码为https://plnkr.co/edit/4mrL5ReQCSvuzOODixJU?p=preview

我没有看到任何合并成功的证据,并且nameResults仅给出名称,而不给出同义词。没有错误消息出现。

http://reactivex.io/documentation/operators/merge.html的RxJS代码使用形式

const mergedResults = Rx.Observable.merge(nameResults, synonymResults); 

的代码,但是当我尝试这样的代码我得到一个错误标记:

找不到名称 'RX'

所以我的问题是:

如何使用RxJS merg在这里?

有没有其他方法使用RxJS更适合?

有没有其他的方法可以在Hero类中工作,比如计算namePlusSynonym而不必为Hero数组中的每一行添加namePlusSynonym?

附录:下面的建议和代码在http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-merge我用控制台输出创建了一个新版本的hero-search.service.ts。

import { Injectable } from '@angular/core'; 
import { Http } from '@angular/http'; 

import { Observable } from 'rxjs/Observable'; 
import 'rxjs/add/operator/map'; 
import 'rxjs/add/operator/merge'; 

import { Hero } from './hero'; 

@Injectable() 
export class HeroSearchService { 

    constructor(private http: Http) {} 

    search(term: string): Observable<Hero[]> { 
    const nameResults = this.http.get(`api/heroes/?name=${term}`); 
    nameResults.subscribe(nameData => console.log('nameData', nameData)); 
    const synonymResults = this.http.get(`api/heroes/?synonym=${term}`); 
    synonymResults.subscribe(synonymData => console.log('synonymData', synonymData)); 
    const combinedResults = nameResults.merge(synonymResults); 
    combinedResults.subscribe(combinedData => console.log('combinedData', combinedData)); 
    return combinedResults.map(response => response.json().data as Hero[]); 
    } 
} 

但它只显示synonymResults,而不是nameResults。 nameResults和synonymResults在控制台输出中对我看起来不错,但combinedResults只有数据和同义词结果的URL,并且似乎不受合并方法的影响。

我也尝试与concat,具有类似的结果。

看来,我不是在合适的阶段组合,但在不同阶段结合的结果相同,只显示同义词:

search(term: string): Observable<Hero[]> { 
    const nameResults = this.http.get(`api/heroes/?name=${term}`).map(response => response.json().data as Hero[]); 
    nameResults.subscribe(nameData => console.log('nameData', nameData)); 
    const synonymResults = this.http.get(`api/heroes/?synonym=${term}`).map(response => response.json().data as Hero[]); 
    synonymResults.subscribe(synonymData => console.log('synonymData', synonymData)); 
    const combinedResults = nameResults.merge(synonymResults); 
    combinedResults.subscribe(combinedData => console.log('combinedData', combinedData)); 
    return combinedResults; 
    } 
+0

使用两个HTTP请求然后在客户端合并它们似乎有点不寻常。通常我会期望看到类似'api/heroes?synonym = $ {synonym}&name = $ {name}'的东西。然而,我认为你缺少的一件事是'nameResults = nameResults.merge(synonymResults);'你忽略了合并的返回值,这是合并的观察值。 – Pace

+0

或者更好的办法是:'let combinedResults = nameResults.merge(synonymResults)',然后返回'combinedResults.map(...)' – Pace

+0

将最后两行改为:const combinedResults = nameResults。合并(synonymResults); return combinedResults.map(response => response.json()。data as Hero []);'结果在同义词工作,但不是名称。 –

回答

0

要获得合并运营工作,你需要导入观察到的,从rxjs合并,只是因为它是在plunker:

import { Observable } from 'rxjs/Observable'; 
import 'rxjs/add/operator/merge'; 

如何使用一个在这里RxJS合并?

我觉得这里你的主要的误解是,呼吁可观察到的回报新的合并观察到merge()。由于rxjs功能pure,称它在这里没有发生变异namedResults

nameResults.merge(synonymResults); 

相反,你应该捕获结果在一个新的常数

const combinedResults = nameResults.merge(synonymResults); 
return combinedResults; 

是否有使用RxJS是比较一些其他的办法适当?

这是我如何解决得到它不改变现有角分量的工作,只有改变hero-search.service.ts问题(plunker here)。通过将操作符链接在一起,可以让这看起来更好看,但我认为在每个阶段查看有用的变量名称和类型对学习非常有帮助。您可以在任何可观测值上放置中间值subscribe() & console.log()以进行测试,以查看该值的实际外观。

search(term: string): Observable<Hero[]> { 
    // get the response observable from requesting the backend  
    const nameResults: Response = this.http.get(`api/heroes/?name=${term}`); 
    // boil that response down to just the data array we care about 
    const nameResultsArr: Observable<Hero[]> = nameResults.map(res => res.json().data); 
    // do the same for the synonym request 
    const synonymResults: Response = this.http.get(`api/heroes/?synonym=${term}`); 
    const synonymResultsArr: Observable<Hero[]> = synonymResults.map(res => res.json().data); 

    // combine the two observable arrays into a stream of arrays 
    const combinedHeroListObservables: Observable<Hero[]> = 
    nameResultsArr.merge(synonymResultsArr); 
    // concat the observable arrays onto each other 
    const concatenatedArrays: Observable<Hero[]> = 
    combinedHeroListObservables.reduce((accumulator, current) => accumulator.concat(current)); 

    // return the combined array 
    return concatenatedArrays; 
} 

但是当我尝试这样的代码,我得到标志的错误: Cannot find name 'Rx'

如果你不关心不必要导入你不使用的东西,你能得到这个使用

import * as Rx from 'rxjs/Rx'; 

但是,最好的做法是只导入您需要的运算符/类型,如plunker示例中所示。

PS:就像在评论中说的一样,有一条休息路线可以返回两个结合的结果,所以客户端不必向服务器进行两次往返。但是,你不能这么做,因为你正在使用Angular团队的例子。

+0

谢谢您的详细回复。这确实在Plunker中运行,但是当我将它放入VSCode时,我得到8个错误,程序无法启动。 nameResults标记为Type'Observable '不可分配给'Response'类型。在'Observable '类型中缺少属性'body'。 地图被标记为属性“地图”在类型“响应”中不存在。 const combinedHeroListObservables:Observable []> = nameResultsArr.merge(synonymResultsArr); –

+0

和combinedHeroListObservables标记为Type'Observable '不可分配为类型'Observable []>''。 类型'Hero []'不可分配为键入'Observable []'。 类型'英雄'不能指定为'Observable '。 类型'英雄'中缺少属性'_isScalar'。 –

+0

我不知道是否导入'rxjs/add/observable/from';是需要的或从别的东西中遗留下来的。 –