46 votes

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

J'essaie de récupérer des données via REST avec angular 2 HttpClient. Je suis le tutoriel angulaire ici. https://angular.io/tutorial/toh-pt6 et sous la Héros et HTTP vous verrez ce bout de code utilisé pour récupérer les données des 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, 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 il y a une ligne rouge sur l'appel response.json() et même quand j'essaie de construire en utilisant ng construire Je reçois l'erreur.

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

Vous pouvez remarquer qu'au lieu de json().data J'ai json().results . C'est parce que, selon le tutoriel, le serveur a répondu avec un objet qui a une valeur de data mais mon propre serveur répond avec un objet qui a un champ results champ. Si vous faites défiler un peu le tutoriel, vous verrez ce point.

Notez la forme des données que le serveur renvoie. Cet exemple particulier Cet exemple d'API Web en mémoire renvoie un objet avec une propriété de données. Votre API peut renvoyer autre chose. Adaptez le code à votre API WEB.

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

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

Lorsque j'ai fait cela, la méthode .json() a été résolue mais une autre erreur s'est affichée, indiquant que

La propriété results n'existe pas sur le type Promise

J'ai essayé de corriger cela en définissant une interface

interface OrderResponse {
    orders: Order[];
}

Et modifié l'appel au get pour

 .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 'Response'.

Une chose à noter est que, dans le tutoriel, ils ont utilisé le Angular HttpModule mais dans mon application, j'utilise le nouveau Angular HttpClientModule donc peut-être que c'est là que l'erreur vient.

Je suis nouveau dans Angular 2 et c'est la première application que je construis avec. Si le code ci-dessus n'est plus valable avec le nouveau HttpClientModule, j'apprécierais toute aide sur la façon de réaliser la même chose avec le nouveau HttpClientModule.

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

Mise à jour

Comme le suggèrent les commentaires, il n'y a pas de méthode .json() dans le nouveau HttpClientModule. J'aimerais quand même avoir de l'aide sur la façon d'obtenir le même effet avec le nouveau module. Dans le guide, ils ont fait quelque chose comme ceci

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 ce code ne se trouve pas dans un composant mais dans un service, donc appeler subscribe et assigner le résultat à un champ d'instance n'a pas beaucoup de sens.

J'ai besoin que mon service renvoie un tableau de commandes enveloppé dans une Promise. Mes composants peuvent simplement faire des appels comme

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

J'ai également pensé à déclarer une variable locale dans ma méthode de récupération de service afin de pouvoir 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 pas beaucoup de sens pour moi car l'appel à .get() est asynchrone et la méthode peut revenir avant même que toutes les données soient récupérées et le tableau des commandes peut être vide.

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

4 votes

Il n'existe pas de méthode .json() dans l'API HttpClient. Le tutoriel que vous suivez utilise l'ancienne API Http. Veuillez vérifier angular.io/guide/http

3 votes

Comme l'a dit @Jota.Toledo, il existe deux clients http - Http et HttpClient. HttpClient est le nouveau client, qui n'utilise plus la méthode .json().

0 votes

@matmo merci pour votre contribution. J'ai mis à jour ma question. Veuillez y jeter un coup d'œil. Merci

128voto

Voicu Points 2818

Pour les futurs visiteurs : Dans le nouveau HttpClient (Angular 4.3+), la fonction response est JSON par défaut, vous n'avez donc pas besoin d'effectuer les opérations suivantes response.json().data plus. Utilisez simplement response directement.

Exemple (modifié à partir 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 d'inclure le module sous le nom de importations dans le dossier de votre projet app.module.ts :

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

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

2 votes

Une explication vraiment simple avec un exemple simple et clair ! Si chacun pouvait donner des réponses aussi utiles, ce serait formidable.

12voto

Jota.Toledo Points 11554

MISE À JOUR : pour rxjs > v5.5

Comme mentionné dans certains commentaires et d'autres réponses, par défaut, l'option 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 canaliser le résultat. C'est pourquoi il n'y a pas de json() plus de méthode.

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

Remarquez que vous pourriez facilement transformer le retour Observable à un Promise en invoquant simplement toPromise() .

RÉPONSE ORIGINALE :

Dans votre cas, vous pouvez

En supposant que votre backend renvoie quelque chose comme :

{results: [{},{}]}

au format JSON, où chaque {} est un objet sérialisé, vous aurez besoin de ce qui suit :

// 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 cela pourrait être archivé par un intercepteur HTTP. Vérifier les docs . Par exemple :

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

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

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

0 votes

Merci. Il semble que le code suppose que mon serveur renvoie un tableau de commandes. Mon serveur renvoie en fait un objet avec un champ .results qui est le tableau des commandes. Quelque chose comme {results : []}.

0 votes

Je mettrai à jour, @ivange94 mais je pense que le mieux serait de supprimer ce niveau de capsulation sur votre backend.

0 votes

De plus, catch renvoie une erreur car la propriété catch n'existe pas sur le type Observable<Order[]>.

1voto

@Voicu a la bonne solution et merci pour l'astuce.

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

0voto

Griffin Points 1

Fix for me on dotnet API angular MVC application

vérifiez l'url de votre api, la queue /api/ a été supprimé lorsque j'ai mis à jour l'url. Sans cela, l'url racine renvoie du html, c'est la raison pour laquelle je ne l'ai pas supprimé. <

-2voto

kagiso william Points 9

L'autre façon d'aborder le problème est d'utiliser cet extrait de code :

JSON.parse(JSON.stringify(response)).data

Ça semble si mal, mais ça marche.

2 votes

Ce n'est pas une bonne méthode, parce que du côté de la réception, il faut à nouveau analyser le json pour l'utiliser.

2 votes

Cette méthode n'est pas utile. J'utilise ce code : "data as [any]" après avoir résolu mes problèmes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X