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?
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>
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.