I am trying to merge 2 HTTP get searches in Angular/TypeScript, but I'm having trouble getting it to work.
The situation is illustrated using the Angular 2 / 4 Tour of Heroes tutorial, which uses an Observable to search heroes for by"name" value (https://angular.io/tutorial/toh-pt6#add-the-ability-to-search-by-name). The code in hero-search.service.ts is as follows:
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[]);
}
}
I need to do the equivalent of adding a second string value for "synonym" to the Hero class:
export class Hero {
id: number;
name: string;
synonym: string;
}
and add a synonym to each Hero:
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};
}
}
I can then search in hero-search.service.ts by synonym by substituting:
.get(`api/heroes/?synonym=${term}`)
My question is how to search for both name and synonym and return matches with either name or synonym.
From the ReactiveX documentation it seems like the answer is to use the Merge operator: http://reactivex.io/documentation/operators/merge.html. However, I'm having trouble getting this to work. I've tried various ways of using merge, including the following new version of the hero-search.service.ts code above:
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[]);
}
}
The not-working-properly code is at https://plnkr.co/edit/4mrL5ReQCSvuzOODixJU?p=preview.
I don't see any evidence that the merge succeeded, and nameResults give only names, not synonyms. No error messages appear.
The RxJS code at http://reactivex.io/documentation/operators/merge.html uses code of the form
const mergedResults = Rx.Observable.merge(nameResults, synonymResults);
but when I try such code I get an error flagged:
Cannot find name 'Rx'
So my questions are:
How can one use RxJS merge here?
Is there some other approach using RxJS that is more suitable?
Is there some other approach at the level of the Hero class that will work such as computing namePlusSynonym without having to add namePlusSynonym to each line in the Hero array?
ADDENDUM: following suggestions here and code at http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-merge I created a new version of hero-search.service.ts with console outputs.
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[]);
}
}
But it only displays synonymResults, not nameResults. Both nameResults and synonymResults look OK to me in the console output, but combinedResults has only data and the url for synonym results and seems unaffected by the merge method.
I also tried with concat, with similar results.
It seems that I'm not combining at the right stage, but combining at a different stage gives the same result, only showing synonyms:
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;
}
api/heroes?synonym=${synonym}&name=${name}. However, I think one thing you are missing isnameResults = nameResults.merge(synonymResults);You are ignoring the return from merge which is the merged observable. - Pacelet combinedResults = nameResults.merge(synonymResults)and then returncombinedResults.map(...)- Paceconst combinedResults = nameResults.merge(synonymResults); return combinedResults.map(response => response.json().data as Hero[]);results in synonym working but not name. - Mickey Segallet combinedResults = nameResults.merge(synonymResults).reduce((a, b) => a.concat(b));- Pace