2017-09-06 48 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';是需要的或從別的東西中遺留下來的。 –