Reputation: 113
I'm trying to make an http request to a server in Angular 2, but the .map() part doesn't seem to be working, even though no error is shown.
Here's my component:
**import { Component, OnInit } from '@angular/core';
import {Song} from '../interfaces/Song';
import { HttpClient } from '@angular/common/http';
import {Http, Response} from '@angular/http';
import {Observable} from 'rxjs/Rx';
import { Constants } from '../app.constants';
export class PlayerComponent implements OnInit {
query;
results;
constructor(private http: HttpClient) {}
ngOnInit() {
}
searchSoundCloud(query: string) {
const maxResults = 100;
query = encodeURIComponent(query.replace(/ /gi, '+'));
const url = `https://api.soundcloud.com/tracks.json?client_id=${Constants.API_KEY}&q=${query}&limit=${maxResults}&linked_partitioning=1`;
this.http
.get(url)
.map(res => this.handleResponse(res)) /// <-- this.handleResponse doesn't get fired!!!
.catch((error) => {
if (error.status === 500) {
return Observable.throw(new Error(error.status));
}
else if (error.status === 400) {
return Observable.throw(new Error(error.status));
}
else if (error.status === 409) {
return Observable.throw(new Error(error.status));
}
else if (error.status === 406) {
return Observable.throw(new Error(error.status));
}
});
}
handleResponse(res: any): any{
var data = res.json();
var result = [];
if (data && data.collection) {
data.collection.forEach(function(item) {
var song: Song = <Song>{};
song.streamUrl = item.stream_url;
song.name = item.title;
song.artist = item.user.username;
song.provider = 1;
song.idFromProvider = item.id;
song.duration = item.duration;
song.imageUrl = item.artwork_url;
song.link = item.permalink_url;
result.push(song);
console.log(result)
});
}
return result;
}
}**
Any idea why?
Upvotes: 1
Views: 11670
Reputation: 61
Use .pipe
with .map
Example:
.map(x => x.json());
instead you can use this:
.pipe(map(x => x.json()));
Upvotes: 0
Reputation: 8904
When you do the map you can just do this kind of thing. song-service.ts.
import { Http, Headers, RequestOptions, Response, URLSearchParams } from '@angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
...
export class SongService {
constructor(private http:Http) {}
getSongs() : Observable<Song[]> {
this.http.get(url)
.map((res:Response) => res.json() as Observable<Song[]>
}
}
You'd include the http in a service layer returning the Observable.
You should look a little more at the other Http package classes too I included a few in the import statements to give you some inspiriation. You can get better type checking that way in your IDE. The HTTP get method can also take RequestOptions, which can be constructed with a Header (useful for things like Basic Authentication), and optionally URLSearchParams (you could add that to yours). You create a new SearchParams variable then use .set method to assign properties..
app-module.ts
import { HttpModule } from '@angular/http'
@NgModule({
...
imports: [ HttpModule]
...
providers:[SongService]
})
export class AppModule {}
In the mycomponent.ts you can do:
export class myComponent implements ngOnInit {
songs$: Observable<Song[]>;
constructor(songService:SongService) {}
...
onInit() {
this.songs$ = this.songService.getSongs()
}
mycomponent.html
<ul *ngFor="let song of songs$ | async">
<li>{{song.duration}}</li>
</ul>
If you do use subscribe you will have a stateful component and, you can get into problems sometimes if you don't unsubscribe.
Async pipe makes that problem go away and you have a stateless component.
See this video for a bit more on async pipe.
You can sometimes get null pointer references with async pipe, so use elvis operator if things go awry like {{song?.duration}}
.
For a stateful component, the sort of syntax you the usually have in mycomponent.ts would be:
export class myComponent implements ngOnInit, ngOnDestroy {
songs$: Observable<Song[]>;
songs: Song[];
onInit() {
this.songs$ = this.songService.getSongs().
subscribe(songs => {this.songs = songs},
error=> {this.songs = [] as Song[];
console.error(error);
switch (error.status)
{
case "400":
case "406":
case "409":
case "500":
// do something
break;
}
}
);
}
onDestroy() {
this.song$.unsubscribe();
}
At that point async pipe is not required in html as you can use songs array state variable.
<ul *ngFor="let song of songs">
<li>{{song.duration}}</li>
</ul>
Upvotes: 2
Reputation: 133
As the comments indicate, a ".subscribe()" from wherever you want to consume the data is necessary to "activate" the Observable. To expand on this, what you're seeing is the effect of "cold" observables – the emitter doesn't emit its first value until it is asked for, at which point it becomes "hot".
This can often be surprising for people coming from Angular 1.x and Promises (which produce values as soon as they've been called). Even if nothing consumes the value, it will still be brought into existence; this is not the case with Observables, which makes them very efficient vehicles for requesting remote data and not overloading with server calls.
Upvotes: 0
Reputation:
1.)
are you calling the method searchSoundCloud
somewhere? Because you have a ngOnInit()
method, but I don't see the call for searchSoundCloud
.
2.) You must subscribe to observables. If you don't subscribe, they want "do" their logic. Example:
let observable = Observable.create(observer => {
console.log('foobar');
observer.complete();
});
This won't log the text "foobar". You have to call subscribe on it:
observable.subscribe(() => {});
Only now, "foobar" will appear in your logs.
So you should make sure, that you call the searchSoundCloud
method somewhere and also call subscribe
on the observable, after the .catch()
handler:
this.http
.get(url)
.map(res => this.handleResponse(res)) /// <-- this.handleResponse doesn't get fired!!!
.catch((error) => {
if (error.status === 500) {
return Observable.throw(new Error(error.status));
}
else if (error.status === 400) {
return Observable.throw(new Error(error.status));
}
else if (error.status === 409) {
return Observable.throw(new Error(error.status));
}
else if (error.status === 406) {
return Observable.throw(new Error(error.status));
}
})
.subscribe((result: any) => {
// do something with the result
})
;
Upvotes: 0