I am trying to make simple web news reader, based mostly on google news api (https://newsapi.org/s/google-news-api). I have done everything like always so services, models etc. but i have problem with mapping my data onto concrete interfaces. Google api returns on one of the endpoint ('top-headlines') data in this format:
{
"status": "ok",
"totalResults": 38,
"articles": [
{
"source": {
"id": "the-new-york-times",
"name": "The New York Times"
},
"author": null,
"title": "Hong Kong Protesters Clash with Police Amid Fears of Mob
Violence - The New York Times",
"description": "some description",
"url": "https://www.nytimes.com/2019/08/11/world/asia/hong-kong-
protest.html",
"urlToImage":
"https://static01.nyt.com/images/2019/08/11/world/11hongkong15/
11hongkong15-facebookJumbo.jpg",
"publishedAt": "2019-08-11T11:19:03Z",
"content": "some content… [+751 chars]"
},
(...and many many more articles)
]
}
I need only articles. And i know that if i get this data from api like that i can call myvariablename.articles, but this does not seems right to me.
My attempt on first was to map in with rxjs map method. But i get error in console that there are is no property articles in Articles model. So I prepared response model and nested articles as property inside response model (interface). And i get exacly the same error. I was thinking that maybe there is some problem with defined in method Observable so i swiched to Response but with no luck, also right now i am getting error from my component where i am subscribing and using service method which says that: "Type Response[] is not assignable to type Article[]"
top-news.service.ts
(...all imports)
export class TopNewsService {
(... ommited defining of variables like url etc.)
public getAll(): Observable<Response[]> {
return this.httpClient
.get<Response[]>(`${this.url}/${this.endpoint}?country=us&apiKey=${this.token}`)
.pipe(
map(response => response.articles),
catchError(this.handleError)
);
}
(...error handling)
}
article.ts
export interface Article {
source: {
id: string;
name: string;
};
author: string;
title: string;
description: string;
url: string;
urlToImage: string;
publishedAt: string;
content: string;
}
response.ts
import { Article } from './article';
export interface Response {
status: string;
totalResults: number;
articles: Article;
}
top-news-component.ts
(...all imports)
export class TopNewsComponent implements OnInit {
articles: Article[];
constructor(private http: HttpClient, private topNewsService: TopNewsService) { }
ngOnInit() {
this.getAllProjects();
}
getAllProjects() {
this.topNewsService.getAll().subscribe(data => {
this.articles = data;
console.log(this.articles)
},
(err: string) => {
alert(err);
});
}
}
I would like to be able to map data inside service and return only articles and then (if this is possible) get this data inside my component and assign it to created variable with defined type Article[].
You're using
this.httpClient.get<Response[]>
That means that you expect Google to return an array of Response. But that's not what it returns. It returns a single Response
. So it should be
this.httpClient.get<Response>
And since you (try to) map this response to an array of articles, then the observable that your service returns is an Observable<Article[]>
, not an Observable<Response[]>
. So it should be
public getAll(): Observable<Article[]> {
This interface
export interface Response {
status: string;
totalResults: number;
articles: Article;
}
is also wrong. A response doesn't contain a single article. It contains an array of article. Look at the JSON:
{
"status": "ok", // one status string
"totalResults": 38, // one totalResults number
"articles": [ // an **array** of articles
..
]
}
So it should be
export interface Response {
status: string;
totalResults: number;
articles: Article[];
}