问题
I\'m trying fetch data via REST with angular 2 HttpClient. I\'m following the angular tutorial here https://angular.io/tutorial/toh-pt6 and under the Heroes and HTTP section you\'ll see this snippet of code used to fetch hero data via http.
getHeroes(): Promise<Hero[]> {
return this.http.get(this.heroesUrl)
.toPromise()
.then(response => response.json().data as Hero[])
.catch(this.handleError);
}
And below is a similar version I wrote in my application
fetch(startIndex: number, limit: number): Promise<Order[]> {
let url = this.baseUrl + \"&startIndex=\" + startIndex + \"&limit=\" + limit;
return this.http.get(url)
.toPromise()
.then(response => response.json().results as Order[])
.catch(this.handleError);
}
I\'m using InteliJ Idea and there\'s a red line on the call response.json() and even when I try to build using ng build I get the error.
Property \'json\' does not exist on type \'Object\'.
You may notice that instead of json().data
I have json().results
. That\'s because according to the tutorial the server responded with an object that has a data
field but my own server responds with an object that has a results
field. If you scroll down the tutorial a bit you\'ll see this point.
Note the shape of the data that the server returns. This particular in-memory web API example returns an object with a data property. Your API might return something else. Adjust the code to match your web API.
In an attempt to fix this, I tried something like this
(response: Response) => response.json().results as Order[]
When I did that the .json() method was been resolved but another error popped up that
Property results does not exist on type Promise
I tried fixing that by defining an interface
interface OrderResponse {
orders: Order[];
}
And modified the get call to
.get<OrderResponse>(url)...
But that also didn\'t work. Another error popped up
Type \'OrderResponse\' is not assignable to type \'Response\'.
One thing note is that, in the tutorial they used the Angular HttpModule but in my application I\'m using the new Angular HttpClientModule so maybe that\'s where the error is coming.
I\'m new to Angular 2 and this is the first app I\'m building with it. If the above code is no longer valid with the new HttpClientModule I\'d appreciate any help on how to achieve the same with the new HttpClientModule.
I found similar questions Property \'json\' does not exist on type \'{}\' and Property does not exist on type \'object\' but none of the answers there helped me.
Update
As the comments suggested there is no .json() method in the new HttpClientModule. I\'d still appreciate help on how to achieve the same effect with the new module. From the guide they did something like this
http.get<ItemsResponse>(\'/api/items\').subscribe(data => {
// data is now an instance of type ItemsResponse, so you can do this:
this.results = data.results;
});
Which I understand perfectly but my problem is, that code is not within a component but a service so calling subscribe and assigning the result to a instance field won\'t make much sense.
I need my service to return an array of Orders wrapped in a Promise. The my components can just make calls like
this.orderService.fetch(0, 10).then(orders => this.orders = orders)
I also thought of declaring a local variable in my service fetch method so that I can do
.subscribe(data => {
this.orders = data.results;
}
// and out of the get call I return my local variable orders like
return Promise.resolve(orders)
But that doesn\'t make much sense to me as the call to .get() is asynchronous and the method may return even before all the data is fetch and the orders array may be empty.
Update
As requested here is the code for handleError
private handleError(error: any): Promise<any> {
console.log(\'An error occured \', error);
return Promise.reject(error.message || error);
}
回答1:
UPDATE: for rxjs > v5.5
As mentioned in some of the comments and other answers, by default the HttpClient deserializes the content of a response into an object. Some of its methods allow passing a generic type argument in order to duck-type the result. Thats why there is no json()
method anymore.
import {throwError} from 'rxjs';
import {catchError, map} from 'rxjs/operators';
export interface Order {
// Properties
}
interface ResponseOrders {
results: Order[];
}
@Injectable()
export class FooService {
ctor(private http: HttpClient){}
fetch(startIndex: number, limit: number): Observable<Order[]> {
let params = new HttpParams();
params = params.set('startIndex',startIndex.toString()).set('limit',limit.toString());
// base URL should not have ? in it at the en
return this.http.get<ResponseOrders >(this.baseUrl,{
params
}).pipe(
map(res => res.results || []),
catchError(error => _throwError(error.message || error))
);
}
Notice that you could easily transform the returned Observable
to a Promise
by simply invoking toPromise()
.
ORIGINAL ANSWER:
In your case, you can
Assumming that your backend returns something like:
{results: [{},{}]}
in JSON format, where every {} is a serialized object, you would need the following:
// Somewhere in your src folder
export interface Order {
// Properties
}
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import { Order } from 'somewhere_in_src';
@Injectable()
export class FooService {
ctor(private http: HttpClient){}
fetch(startIndex: number, limit: number): Observable<Order[]> {
let params = new HttpParams();
params = params.set('startIndex',startIndex.toString()).set('limit',limit.toString());
// base URL should not have ? in it at the en
return this.http.get(this.baseUrl,{
params
})
.map(res => res.results as Order[] || []);
// in case that the property results in the res POJO doesnt exist (res.results returns null) then return empty array ([])
}
}
I removed the catch section, as this could be archived through a HTTP interceptor. Check the docs. As example:
https://gist.github.com/jotatoledo/765c7f6d8a755613cafca97e83313b90
And to consume you just need to call it like:
// In some component for example
this.fooService.fetch(...).subscribe(data => ...); // data is Order[]
回答2:
For future visitors: In the new HttpClient (Angular 4.3+), the response
object is JSON by default, so you don't need to do response.json().data
anymore. Just use response
directly.
Example (modified from the official documentation):
import { HttpClient } from '@angular/common/http';
@Component(...)
export class YourComponent implements OnInit {
// Inject HttpClient into your component or service.
constructor(private http: HttpClient) {}
ngOnInit(): void {
this.http.get('https://api.github.com/users')
.subscribe(response => console.log(response));
}
}
Don't forget to import it and include the module under imports in your project's app.module.ts:
...
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [
BrowserModule,
// Include it under 'imports' in your application module after BrowserModule.
HttpClientModule,
...
],
...
回答3:
@Voicu has the right solution and thank you for the hint.
For future visitors: In the new HttpClient (Angular 4.3+), the response object is JSON by default, so you don't need to do response.json().data anymore. Just use response directly.
来源:https://stackoverflow.com/questions/46005430/property-json-does-not-exist-on-type-object