Angular 2 way to get an element from Observable <Xyz []>
Given the following Typescript in an Angular 2 service:
getLanguages () { return this.http.get(this._languagesUrl) .map(res => <Language[]> res.json().data) .catch(this.handleError); I am having difficulty using this in cases where I need to search for a specific element from an array. For example, I cannot do the following, because filter expects an Observable<Language> instead of the returned Observable<Language[]> .
getLanguages().filter(language => language.id == 3) // Error I understand that my problem may be that I am mixing synchronous and asynchronous behavior, so I provide my use case: the user can enter the language identifier, and I want to display the name of the associated language. I want to use getLanguages() with an Observable result because it is already in use elsewhere in the project. I also want to implement some caching, so the HTTP request is not received every time I search.
Any thoughts?
Here is an example of what you want to do:
https://plnkr.co/edit/lK47pVaW8b0CEez0mum4?p=preview
Press F12 in chrome to view the logs to give you a clearer idea of what is happening and why this does not work in your example.
Pay particular attention to:
constructor(private _langService: LanguagesService) { _langService.getLanguages() //We get an Observable<Array> object returned. //So this is the observable filter function: .filter( this._filter3rdLanguage ) //The filter gets called only once and its comparing an observable object, not a language object. //that why nothing gets filtered: .do( o => console.log(o) ) //If you filter the actual list instead of the observable object, you'll get it called several times. //This is the Array filter function. .subscribe( list => this.languages = list.filter( this._filter3rdLanguage ) ); } This is another, possibly the best way to do this:
_langService.getLanguages() .map( list => list.filter(this._filter3rdLanguage) ) //see that this one IS filtered. .do( list => console.log(list) ) .subscribe( list => this.languages = list ); element search in Observable <Type>
let item: Language; langugages. // observable cached data .subscribe((items: Language[]) => item = items.find(p => p.id == 3)); You can use mergeMap instead of map to smooth the array:
getLanguages () { return this.http.get(this._languagesUrl) .flatMap(res => Rx.Observable.fromArray(<Language[]> res.json().data)) .catch(this.handleError); } Now the return value from getLanguages will be Observable<Language>
If you follow the map method with subscribe , you can access the members of the Language array when the array is available:
return this.http.get(this._languagesUrl) .map(res => <Language[]> res.json().data) .subscribe((languages: Language[]) => alert(languages[3]); ); This is something to guess, since I personally have not used RxJS, although I am interested in this. Looking through the documents, I wonder if the following will work:
function getLanguageName(id: number): Observable<string> { return getLanguages() .map((langs: Language[]) => _.find(langs, { id: id })) .pluck('name'); } I used Lodash _.find() to find the corresponding entry in the array, but if you do not have one, you can always search manually or use .filter(langs, ...)[0] .
If you want to do caching, and you know that the language name for this identifier will never change, you can use the Lodash _.memoize() function to reuse the output for the given input.
I am interested to know if this works for you.
For the customer service application that I support, I need a way to hide or show the buttons depending on whether the user account had a specific area of activity, and I needed to work on the asynchronization data loaded on the pages, and it was not necessary to use the data as there is. So I call the method from the template and using the map with find, then I sign up.
<button *ngIf="(lobs$ | async) ? getButtonVisibility('internet') : false" class="eing-account-header-drawers-button" (click)="openDrawer(drawers[drawers.internet],$event)"> public getButtonVisibility(lobName: string) { let isVisible; this.lobs$.map(x => x.findIndex(y => y.lobName.toLowerCase() == lobName.toLowerCase() && y.hasLob)) .subscribe(x => { isVisible = x != -1}); return isVisible; }