Simple filter on array of RXJS Observable
I am starting my project with Angular2 and the developers seem to recommend RXJS Observable instead of Promises.
I have achieved to retrieve a list of elements (epics) from the server. But how can I filter the elments by using for example an id?
The following code is an extraction from my app and shows now the final working solution. Let's hope it helps someone.
@Injectable()
export class EpicService {
private url = CONFIG.SERVER + '/app/'; // URL to web API
constructor(private http:Http) {}
private extractData(res:Response) {
let body = res.json();
return body;
}
getEpics():Observable<Epic[]> {
return this.http.get(this.url + "getEpics")
.map(this.extractData)
.catch(this.handleError);
}
getEpic(id:string): Observable<Epic> {
return this.getEpics()
.map(epics => epics.filter(epic => epic.id === id)[0]);
}
}
export class EpicComponent {
errorMessage:string;
epics:Epic[];
epic:Epic;
constructor(
private requirementService:EpicService) {
}
getEpics() {
this.requirementService.getEpics()
.subscribe(
epics => this.epics = epics,
error => this.errorMessage = <any>error);
}
// actually this should be eventually in another component
getEpic(id:string) {
this.requirementService.getEpic(id)
.subscribe(
epic => this.epic = epic,
error => this.errorMessage = <any>error);
}
}
export class Epic {
id: string;
name: string;
}
Thank you in advance for your help.
Solution 1:
You'll want to filter the actual array and not the observable wrapped around it.
So you'll map the content of the Observable (which is an Epic[]
) to a filtered Epic
.
getEpic(id: string): Observable<Epic> {
return this.getEpics()
.map(epics => epics.filter(epic => epic.id === id)[0]);
}
Then afterwards you can subscribe
to getEpic
and do whatever you want with it.
Solution 2:
You can do this using the flatMap
and filter
methods of Observable
instead of the JS array filter method in map
. Something like:
this.getEpics()
.flatMap((data) => data.epics) // [{id: 1}, {id: 4}, {id: 3}, ..., {id: N}]
.filter((epic) => epic.id === id) // checks {id: 1}, then {id: 2}, etc
.subscribe((result) => ...); // do something epic!!!
flatMap
will provide singular indices for filtering and then you can get on with whatever happens next with the results.
If TypeScript throws a error indicating you can't compare a string and a number regardless of your use of ==
in the filter just add a +
before epic.id
in the filter, per the Angular docs:
.flatMap(...)
.filter((epic) => +epic.id === id) // checks {id: 1}, then {id: 2}, etc
.subscribe(...)
Example:
https://stackblitz.com/edit/angular-9ehje5?file=src%2Fapp%2Fapp.component.ts
Solution 3:
original answer with a fix:
Observables
are lazy. You have to call subscribe
to tell an observable
to send its request.
getEpic(id:number) {
return this.getEpics()
.filter(epic => epic.id === id)
.subscribe(x=>...);
}
Update to Rxjs 6:
import {filter} from 'rxjs/operators';
getEpic(id:number) {
return this.getEpics()
.pipe(filter(epic => epic.id === id))
.subscribe(x=>...);
}