web-dev-qa-db-fra.com

La propriété 'json' n'existe pas sur le type 'Object'

J'essaie d'extraire des données via REST avec 2 HttpClient angulaire. Je suis le tutoriel angulaire ici https://angular.io/tutorial/toh-pt6 et sous la section Heroes and HTTP, vous verrez cet extrait de code utilisé pour récupérer les données du héros via http.

getHeroes(): Promise<Hero[]> {
  return this.http.get(this.heroesUrl)
         .toPromise()
         .then(response => response.json().data as Hero[])
         .catch(this.handleError);
}

Et ci-dessous est une version similaire que j'ai écrite dans mon 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);
}

J'utilise InteliJ Idea et la réponse à l'appel est une ligne rouge. Même lorsque j'essaie de construire à l'aide de ng build, l'erreur est générée.

La propriété 'json' n'existe pas sur le type 'Object'.

Vous remarquerez peut-être qu'au lieu de json().data, j'ai json().results. En effet, selon le didacticiel, le serveur a répondu avec un objet doté d'un champ data alors que mon propre serveur répond avec un objet doté d'un champ results. Si vous faites un peu défiler le tutoriel, vous verrez ce point.

Notez la forme des données renvoyées par le serveur. Ce particulier L’exemple d’API Web en mémoire renvoie un objet avec une propriété data. Votre L'API peut retourner quelque chose d'autre. Adaptez le code à votre site Web API.

Pour tenter de résoudre ce problème, j'ai essayé quelque chose comme ça

(response: Response) => response.json().results as Order[]

Quand j’ai fait cela, la méthode .json () a été résolue mais une autre erreur est apparue: 

Les résultats de propriété n'existent pas sur le type Promise

J'ai essayé de résoudre ce problème en définissant une interface

interface OrderResponse {
    orders: Order[];
}

Et modifié l'appel de get à 

 .get<OrderResponse>(url)...

Mais cela n'a pas fonctionné non plus. Une autre erreur est apparue

Le type 'OrderResponse' n'est pas assignable au type 'Réponse'.

Une remarque est que, dans le tutoriel, ils ont utilisé le module angulaire HttpModule, mais que dans mon application, j'utilise le nouveau module angulaire HttpClientModule, c'est peut-être là que l'erreur se produit.

Je suis nouveau sur Angular 2 et c'est la première application que je construis avec. Si le code ci-dessus n'est plus valide avec le nouveau HttpClientModule, j'apprécierais toute aide sur la manière d'obtenir la même chose avec le nouveau HttpClientModule. 

J'ai trouvé des questions similaires La propriété 'json' n'existe pas sur le type '{}' et La propriété n'existe pas sur le type 'objet' mais aucune des réponses ne m'a aidé. 

Mettre à jour

Comme le suggéraient les commentaires, il n'y a pas de méthode .json () dans le nouveau HttpClientModule. J'apprécierais toujours l'aide sur la façon d'obtenir le même effet avec le nouveau module. Du guide ils ont fait quelque chose comme ça

http.get<ItemsResponse>('/api/items').subscribe(data => {
  // data is now an instance of type ItemsResponse, so you can do this:
  this.results = data.results;
});

Ce que je comprends parfaitement mais mon problème est que le code ne fait pas partie d’un composant mais d’un service qui appelle donc s’abonner et attribue le résultat à un champ d’instance n’aura pas beaucoup de sens. 

J'ai besoin de mon service pour renvoyer un tableau de commandes enveloppé dans une promesse. Les composants mes peuvent juste faire des appels comme

this.orderService.fetch(0, 10).then(orders => this.orders = orders)

J'ai aussi pensé à déclarer une variable locale dans ma méthode de récupération de service pour pouvoir le faire.

.subscribe(data => {
    this.orders = data.results;
}
// and out of the get call I return my local variable orders like
return Promise.resolve(orders)

Mais cela n’a aucun sens pour moi car l’appel de .get () est asynchrone et la méthode peut retourner avant même que toutes les données soient extraites et que le tableau orders soit vide.

Mettre à jour

Comme demandé, voici le code pour handleError

private handleError(error: any): Promise<any> {
    console.log('An error occured ', error);
    return Promise.reject(error.message || error);
}
23
ivange94

UPDATE: pour rxjs> v5.5

Comme mentionné dans certains commentaires et autres réponses, par défaut, HttpClient désérialise le contenu d'une réponse dans un objet. Certaines de ses méthodes permettent de passer un argument de type générique afin de taper le résultat. C'est pourquoi il n'y a plus de méthode json().

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))
    );
} 

Notez que vous pouvez facilement transformer la Observable renvoyée en une Promise en appelant simplement toPromise().

RÉPONSE ORIGINALE:

Dans votre cas, vous pouvez 

En supposant que votre backend retourne quelque chose comme:

{results: [{},{}]}

au format JSON, où chaque {} est un objet sérialisé, vous devez disposer des éléments suivants:

// 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 ([])
  }
} 

J'ai supprimé la section catch, car elle pourrait être archivée via un intercepteur HTTP. Vérifiez la documentation . À titre d'exemple: 

https://Gist.github.com/jotatoledo/765c7f6d8a755613cafca97e83313b90

Et pour consommer, il suffit de l'appeler comme suit:

// In some component for example
this.fooService.fetch(...).subscribe(data => ...); // data is Order[]
7
Jota.Toledo

Pour les futurs visiteurs: Dans le nouveau HttpClient (Angular 4.3+), l'objet response est JSON par défaut, vous n'avez donc plus besoin de response.json().data. Utilisez simplement response directement.

Exemple (modifié de la documentation officielle):

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));
  }
}

N'oubliez pas de l'importer et incluez le module sous imports dans le app.module.ts de votre projet:

...
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  imports: [
    BrowserModule,
    // Include it under 'imports' in your application module after BrowserModule.
    HttpClientModule,
    ...
  ],
  ...
87
Voicu

Pour les futurs visiteurs: dans le nouveau HttpClient (Angular 4.3+), l'objet de réponse est JSON par défaut. Vous n'avez donc plus besoin de response.json (). Data. Il suffit d'utiliser la réponse directement.

0
Nasser Albelbeisi