问题
I have a preject 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?
回答1:
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.
回答2:
Great! You can try with Observable and Async! In app.component
public posts: Observable<posts[]>
getPostsFromService() {
this.posts = this.myService.getPosts();
}
And in html, you put that:
*ngFor="let post of posts | async"
And service
return this._http.get<posts>('https://jsonplaceholder.typicode.com/posts', httpOptions)
Try that and see if worked! I hope that helps!
回答3:
If you desire to use a service methods must return observable response like this:
public getPosts(): Observable<IPosts[]> {
const data = {
'$type' : 'ReadRequest',
'query' : 'getPosts',
'parameters' : {}
};
return this.http.post(null, data)
.map(res => (<any>res)._body === '' ? {} : res.json().result)
.catch(this.handleError);
}
private handleError(error: any): Promise<any> {
return Promise.reject(error.message || 'Server error: ' + error);
}
Where IPosts
is described Interface according your response.
After in any component you can use this:
public posts: IPosts[] = [];
this.servicePosts.getPosts().subscribe(data => {
this.posts = data; // Fill data from response to component variable this.post
}, error => {
console.log(error);
});
回答4:
Your component.ts should be like this.
There is no issue in your service except subscribe should be map.
this.myService.getPosts().subscribe((data) => {
this.setMyData(data);
});
setMyData() {
this.myData = data;
}
来源:https://stackoverflow.com/questions/50570340/angular-6-httpclient-keeping-subscribe-in-the-service-but-passing-the-result-t