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

1 Answer 1

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>
Sign up to request clarification or add additional context in comments.

6 Comments

here is how me service look like .... getArticles(): Observable<Article[]> { return this.http.get<Article[]>("./assets/DB.json") .pipe(catchError(this.handleHttpErrService.handleError)); }
@MOHAMEDABUELATTA: Please post any updates to the question.
@MOHAMEDABUELATTA: You could pipe in pluck before the catchError operator. I've updated the answer.
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
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[])
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.