2017-07-23 214 views
-1

好的,所以我一直在爲此掙扎了一段時間。我使用名爲「Contenful」的服務來充當CMS,並使用它們的API來獲取JSON數據。Angular 2無法從模板訪問HTTP獲得響應

我有一個叫做「editor.service.ts」服務如下(我已經隱藏了「SpaceID」和「訪問令牌」隱私):

import { Injectable } from '@angular/core'; 
import { Http, Response } from '@angular/http'; 
import { Observable } from 'rxjs/Observable'; 
import 'rxjs/add/operator/map'; 

@Injectable() 
export class EditorService { 
    private baseURL = 'https://cdn.contentful.com'; 
    private spaceID = 'HIDDEN'; 
    private accessToken ='HIDDEN'; 
    private blocksURL = this.baseURL + '/spaces/' + this.spaceID + '/entries?access_token=' + this.accessToken + '&content_type=blocks'; 

    constructor(private http: Http) {} 

    getBlocks(): Observable<any> { 
     return this.http.get(this.blocksURL).map((res: Response) => res); 
    } 
} 

然後在我的組件調用'editor.component.ts' 我有這樣的:

import { Component, OnInit } from '@angular/core'; 
import { EditorService } from './../editor.service'; 

@Component({ 
    selector: 'app-editor', 
    templateUrl: './editor.component.html', 
    styleUrls: ['./editor.component.css'], 
    providers: [EditorService] 
}) 

export class EditorComponent implements OnInit { 
    blocksJSON: any; 

    constructor(private editorService: EditorService) {} 

    ngOnInit() { 
     this.editorService.getBlocks().subscribe(res => { 
      this.blocksJSON = res.json().items; 
      console.log(this.blocksJSON); //THIS WORKS 
     }); 
    } 
} 

這裏有一個是越來越我困惑的一點。該文件中的console.log()行正好打印出我想要的內容,所以我知道該API工作正常。令人困惑的是現在。

在我的模板文件,我有這樣一行:

<h1 *ngFor="let block of blocksJSON; let i = index">{{block[i].fields.blockName}}</h1> 

在我看來,這應該只是工作。不過,我不斷收到錯誤

ERROR TypeError: Cannot read property 'fields' of undefined 

它打印出此錯誤3次,即使在目前,有在數組「塊」只有1對象。但我不認爲有1個對象是一個問題...(我可能是錯的,雖然)

我不知道這是爲什麼,因爲console.log打印出我想要的數據,所以我知道它應該被定義。我唯一的想法是角度2 HTTP get是異步的,但我不知道這是否是問題。

任何意見將不勝感激。謝謝。

+1

[angular2的可能重複:錯誤:類型錯誤:無法讀取undefi的屬性'...' ned](https://stackoverflow.com/questions/41242793/angular2-error-typeerror-cannot-read-property-of-undefined) – Alex

+0

和更多的建議... https://stackoverflow.com/questions/34734671/ observable-type-error-can-read-property-of-undefined大量的問答環節;) – Alex

+0

@ AJT_82感謝您的回答並抱歉發佈重複。根據2個答案,我認爲我需要將h1包裝在* ngIf中。我這樣做如下:但我仍然得到相同的錯誤。你有什麼想法,或者我應該繼續尋找:) –

回答

0

正如我提到的,主要的問題是在這裏:

<h1 *ngFor="let block of blocksJSON; let i = index">{{block[i].fields.blockName}}</h1> 

如果blocksJSON是一個數組,ngFor會照顧迭代它爲你的元素,所以你不要這樣需要訪問藉助於元素的指數。

<h1 *ngFor="let block of blocksJSON">{{block.fields.blockName}}</h1> 

這應該工作,只要blocksJSON數組中的每一個元素都有fields屬性。

跟你一樣的角度出發,我會用這個答案指向一些其他的東西,你可以改善:

型號:始終定義一個接口爲你的後端的響應。在地方使用any可以避免擊敗類型腳本的最大目的之一腳本:強打字。

因此,例如,定義如下:

export interface BlockFields{ 
    blockName: string; 
    // more properties 
} 

export interface Block{ 
    fields: BlockFields; 
    .... //more properties 
} 

服務:

import { Response, Http} from '@angular/http'; 
import { Block } from '...' // the place where you saved the response interface 
import { _throw } from 'rxjs/observable/throw'; 
import { ErrorObservable } from 'rxjs/observable/ErrorObservable'; 

@Injectable() 
export class EditorService { 
    private baseURL = 'https://cdn.contentful.com'; 
    private spaceID = 'HIDDEN'; 
    private accessToken ='HIDDEN'; 
    private _blocksUrl = `${this.baseUrl}/spaces/${this.spaceID}/entries?access_token=${this.accessToken}&content_type=blocks`; //template strings makes concatenation of multiple static sections and parameters cleaner 

    constructor(private http: Http) {} 

    getBlocks(): Observable<Block[]> { 
     return this.http.get(this.blocksURL) 
     .map(this.extractData) 
     .catch(this.handleError); // always handle your http errors at service level first. 
    } 

    private extractData(response: Response){ 
     return response.json(); 
    } 

    private handleError(error: any): ErrorObservable { 
    let errMsg: string; 
    if (error instanceof Response) { 
     const body = error.json() || ''; 
     const err = JSON.stringify(body); 
     errMsg = `${error.status} - ${error.statusText || ''} Details: ${err}`; 
    } else { 
     errMsg = error.message ? error.message : error.toString(); 
    } 
    return _throw(errMsg); 
    }; 
} 

組件:

import { Block } from '...' // the place where you saved the response interface 

@Component({ 
    selector: 'app-editor', 
    templateUrl: './editor.component.html', 
    styleUrls: ['./editor.component.css'] 
    //providers: [EditorService] dont provide the service directly in the component decorator. Instead, add the service to the providers array of you app.module. If you provide it here, a new instance of the EditorSerice will be created every time a this component is created. You want services to be singletone. 
})  
export class EditorComponent implements OnInit { 
    blocks: Block[]; 

    constructor(private editorService: EditorService) {} 

    ngOnInit() { 
     this.editorService.getBlocks() 
     .subscribe(res => { 
      this.blocks= blocks; 
     }); 
    } 
} 
+0

感謝您的幫助,不僅是爲了解決這個問題,還有一些其他方面的角度2。我現在可能會去添加這些東西。謝謝 :) –