Angular 6 - HttpClient Keeping subscribe in the service but passing the result to the component

user9855223 picture user9855223 · May 28, 2018 · Viewed 21.1k times · Source

I have a project which contains a service (which gets the data) and a component (displays it).

I would like the keep the code to the minimum on the app.component.ts.

In the service I have:

getPosts() {
    return this.http.get('https://jsonplaceholder.typicode.com/posts', httpOptions).subscribe(
      result => {
        return result;
        this.theData = result;
      },
      error => {
        return error;
      }
    );
  }

and in my app.component.ts:

 getPostsFromService() {
    this.myService.getPosts();
  }

But of course, I need to get the result and pass it into my component but something like this wouldn't work:

myData;

  ...


  getPostsFromService() {
    this.myData = this.myService.getPosts();
  }

So, my question is, how can I do this or is it really recommended to call subscribe on my component and not in the service?

Answer

Vikas picture Vikas · May 28, 2018

In new HttpClientModule JSON is an assumed default and no longer needs to be explicitly parsed using res.json()

You can tell HttpClient the type of the response to make consuming the output easier and more obvious.

Type checking of response can be done by using the type parameter
Create an Interface

export interface Idata{
  userId:number;
  Id:number;
  title:string;
  body:string;
}

Http returns an observable and We can tell the HttpClient.get to return response as Idata type When we use http.get<Idata[]>(...) then it returns the instance of Observable<Idata[]> type.
In your service

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import {Observable} from 'rxjs';
import {Idata} from './Idata'
@Injectable()
export class ShareService {

    constructor(private httpc:HttpClient)
    {
    }

    public getPosts():Observable<Idata[]>
    {
        return this.httpc.get<Idata[]>('https://jsonplaceholder.typicode.com/posts');
    }
}

In your component subscribe to Observable<Idata[]> to get instance of Idata

  public data:Array<Idata>=[];
  constructor(public service:ShareService)
  {
     this.service.getPosts().subscribe(value => {
        this.data=value;
     });
  }

An alternative is Using async pipe AsyncPipe accepts as an argument an observable or a promise, calls subscribe or attaches a then handler, then waits for the asynchronous result before passing it through to the caller.

public response:Observable<Idata[]>;
constructor(public service:ShareService)
{
    this.response=this.service.getPosts();
}

HTML

<div *ngFor="let item of (response|async)">
  {{item.body}}
</div>


LIVEDEMO

my question is, how can I do this or is it really recommended to call subscribe on my component and not in the service?

It's Good Practice to Delegate complex component logic to services

From Angular Style Guide
Do limit logic in a component to only that required for the view. All other logic should be delegated to services.

Do move reusable logic to services and keep components simple and focused on their intended purpose.

Why? Logic may be reused by multiple components when placed within a service and exposed via a function.

Why? Logic in a service can more easily be isolated in a unit test, while the calling logic in the component can be easily mocked.

Why? Removes dependencies and hides implementation details from the component.

Why? Keeps the component slim, trim, and focused.

Subscribing in Component lets you share a single http request with multiple observers, if you don't do so you would be violating DRY Principle
P:S To share a single http request for multiple observers, you need something like the share operator.

import {Observable} from 'rxjs';
import {share} from 'rxjs/operators'

export class dataService {

    public data$=Observable<Idata[]>
    constructor(http:HttpClient)
    {
        this.data$=this.http.get<Idata[]>('https://jsonplaceholder.typicode.com/posts').pipe(share());
    }
    public getPosts():Observable<Idata[]>
    {
        return this.data$
    }
}

This operator is a specialization of publish which creates a subscription when the number of observers goes from zero to one, then shares that subscription with all subsequent observers until the number of observers returns to zero, at which point the subscription is disposed.