0

I made a very normal HTTP request to get one object from a JSON file. but I noticed I get all the file's data instead of the only needed object. following my code demonstrates how I made a class to fetch needed data but it still not working as planned for some reason.

JSON file (DB.json) => I need just (articles)

{
    "articles": [
      {
        "id": "1",
        "title": "Title 1",
        "body": "what ever ... ",
        "date": "14/03/2020",
        "rating": 4,
        "pic": "../../assets/images/cat.jpg"
      },
      {
        "id": "2",
        "title": "Title 2",
        "body": "what ever ... ",
        "date": "15/03/2020",
        "rating": 5,
        "pic": "../../assets/images/dog.jpg"
      }
    ],
    AnotherTableName [ ... etc ],
    AnotherTableName [ ... etc ]
}

article.ts => the class

export class Article {
    id: string;
    title: string;
    body: string;
    date: Date;
    rating: number;
    pic: string;
}

component.ts

export class ArticleComponent implements OnInit {

  constructor(private httpGetArticles: ArticleService) { }
  
  errMess: string;
  articles: Article[];

  ngOnInit(){
    this.httpGetArticles.getArticles().subscribe(
     data =>  this.articles = data,
     errmess => this.errMess = <any>errmess,
     () => console.log(this.articles));
    
  }

}

component.html

<p *ngFor="let artcl of articles">
    <span>artcl.title</span>>
</p>

Service

  getArticles(): Observable<Article[]> {
    return this.http.get<Article[]>("./assets/DB.json")
    .pipe(catchError(this.handleHttpErrService.handleError));
  }

console.log( .. result

Object { 
articles: Array [ {…}, {…} ]
feedback: Array(13) [ {…}, {…}, {…}, … ]
leaders: Array(4) [ {…}, {…}, {…}, … ]
promotions: (1) […]

Despite I get the object as shown, I got also the following error ( I think because I use NgFor for one object named articles but retrieved data come up with a different format "the whole JSON file" )

ERROR Error: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays.

So I need to retrieve the casted data only (articles) not all the JSON file

MOHAMED ABUELATTA
  • 305
  • 2
  • 5
  • 15

1 Answers1

1

Ideally the backend should support returning specific properties of an object. For a quick fix you could pipe in RxJS pluck operator to the HTTP request in the client

Service

import { catchError, pluck } from 'rxjs/operators';

getArticles(): Observable<Article[]> {
  return this.http.get<Article[]>("./assets/DB.json").pipe(
    pluck('articles'),
    catchError(this.handleHttpErrService.handleError)
  );
}

In addition I'd also suggest the following changes.

  1. Use TS interface instead of class for type checking. When to use an interface or a class in Typescript?

article.ts

export interface Article {
  id: string;
  title: string;
  body: string;
  date: Date;
  rating: number;
  pic: string;
}
  1. Use async pipe in the component template instead of a subscription in the controller.

Component

import { throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

export class ArticleComponent implements OnInit {
  constructor(private httpGetArticles: ArticleService) { }
  
  articles$: Observable<Article[]>;

  ngOnInit(){
    this.articles$ = this.httpGetArticles.getArticles().pipe(
      catchError(error => {
        console.log('Error fetching articles:', error);
        return throwError(error);
      }
    );
  }
}

Template

<ng-container *ngIf="(articles$ | async) as articles">
  <div *ngFor="let article of articles">
    ...
  </div>
</ng-container>
ruth
  • 29,535
  • 4
  • 30
  • 57
  • here is how me service look like .... getArticles(): Observable
    { return this.http.get
    ("./assets/DB.json") .pipe(catchError(this.handleHttpErrService.handleError)); }
    – MOHAMED ABUELATTA Mar 31 '21 at 07:14
  • @MOHAMEDABUELATTA: Please post any updates to the question. – ruth Mar 31 '21 at 07:15
  • @MOHAMEDABUELATTA: You could pipe in `pluck` before the `catchError` operator. I've updated the answer. – ruth Mar 31 '21 at 07:17
  • It works but I couldn't able to use 2 pipes in a row like pluck('articles'), catchError ... So that I removed catchError because it gives me an error .. thanks anyway – MOHAMED ABUELATTA Mar 31 '21 at 08:54
  • now it's working but i should change getArticles(): Observable
    { .. to getArticles(): Observable { ..
    – MOHAMED ABUELATTA Mar 31 '21 at 09:52
  • 1
    You should be able to pipe in as many operators as you want. And if you'd like to enforce the type checking instead of using `any`, use `map` operator instead of `pluck` and cast the type: `map((data: any) => data['articles'] as Article[])` – ruth Mar 31 '21 at 09:56