如何在Angular Heroes教程中合并2个搜索



我正在尝试合并2 http http在Angular/tyspript中获取搜索,但是我很难使它工作。

使用Angular 2/4的英雄巡回演出来说明情况,该访问使用可观察到的英雄来搜索"名称"值(https://angular.io/tutorial/tutorial/toh-pt6#add-the----------逐个名称的能力)。英雄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}`)

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

在Ractivex文档中,答案似乎是使用合并操作员:http://reactivex.io/documentation/operators/merge.html。但是,我很难做到这一点。我尝试了使用合并的各种方法,包括以下新版本的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}`);
    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/4mrl5reqccsvuzoodixju?p=preview。

我看不到合并成功的任何证据,纳默尔斯只给出名称而不是同义词。没有出现错误消息。

http://reactivex.io/documentation/operators/merge.html的RXJS代码使用表单的代码

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

但是,当我尝试这样的代码时,我会收到一个错误:

找不到名称'rx'

所以我的问题是:

如何使用RXJ在此处合并?

是否还有其他更合适的RXJ的方法?

在英雄级别的级别上还有其他方法,例如计算nameplussynyny,而不必在英雄数组中的每一行中添加nameplussynyny?

附录:以下建议和http://reactivex.io/rxjs/class/es6/observable.js〜observable.html#instance-method-merge我创建了一个新版本的英雄 - 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[]);
  }
}

,但它仅显示同义词,而不显示名称。NAMERESULTS和同义词都在控制台输出中对我来说还不错,但是合并重新质量仅具有同义词结果的数据和URL,并且似乎不受Merge方法的影响。

我也尝试了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;
  }

要使合并操作员正常工作,您需要从rxjs中导入可观察到的合并,就像在plunker中一样:

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

如何使用RXJ在此处合并?

我认为您在这里的主要误解是在可观察的返回a 新合并可观察的可观察上调用 merge()。由于RXJS功能是纯粹的,因此在此处称其不突变namedResults

nameResults.merge(synonymResults);

相反,您应该在新常数

中捕获结果
const combinedResults = nameResults.merge(synonymResults);
return combinedResults;

是否还有其他更合适的RXJ的方法?

这就是我解决问题的方法(在此处plunker)使它起作用而不更改现有的角度组件,而仅更改hero-search.service.ts。通过将操作员链在一起,您可以使其看起来更好,但是我认为在每个阶段看到具有类型的有用变量名称对学习非常有帮助。您可以放置一个中间subscribe()&amp;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团队的示例。

最新更新