web-dev-qa-db-ja.com

観測可能なタイプエラー:未定義のプロパティを読み取れません

Angular 2アプリケーションで、エラーが発生します。

未定義のプロパティ「タイトル」を読み取ることができません。

これは非常に単純なコンポーネントであり、ここで動作するために最低限必要なものを取得しようとしています。 APIコントローラーに(不思議なことに複数回)ヒットし、オブジェクトが返された後にコールバックにヒットするようです。 console.logには、予想どおりのオブジェクトが出力されます。完全なエラーは次のとおりです。

TypeError: Cannot read property 'title' of undefined
    at AbstractChangeDetector.ChangeDetector_About_0.detectChangesInRecordsInternal (eval at <anonymous> (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:10897:14), <anonymous>:31:26)
    at AbstractChangeDetector.detectChangesInRecords (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8824:14)
    at AbstractChangeDetector.runDetectChanges (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8807:12)
    at AbstractChangeDetector._detectChangesInViewChildren (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8877:14)
    at AbstractChangeDetector.runDetectChanges (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8811:12)
    at AbstractChangeDetector._detectChangesContentChildren (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8871:14)
    at AbstractChangeDetector.runDetectChanges (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8808:12)
    at AbstractChangeDetector._detectChangesInViewChildren (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8877:14)
    at AbstractChangeDetector.runDetectChanges (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8811:12)
    at AbstractChangeDetector.detectChanges (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8796:12)

サービス(about.service.ts):

import {Http} from 'angular2/http';
import {Injectable} from 'angular2/core';
import {AboutModel} from './about.model';
import 'rxjs/add/operator/map';

@Injectable()
export class AboutService {
    constructor(private _http: Http) { }

    get() {
        return this._http.get('/api/about').map(res => {
            console.log(res.json()); // I get the error on the line above but this code is still hit.
            return <AboutModel>res.json();
        });
    }
}

コンポーネント(about.component.ts):

import {Component, View, OnInit} from 'angular2/core';
import {AboutModel} from './about.model';
import {AboutService} from './about.service';
import {HTTP_PROVIDERS} from 'angular2/http';

@Component({
    selector: 'about',
    providers: [HTTP_PROVIDERS, AboutService],
    templateUrl: 'app/about/about.html'
})

export class About implements IAboutViewModel, OnInit {
    public about: AboutModel;

    constructor(private _aboutService: AboutService) {}

    ngOnInit() {    
        this._aboutService.get().subscribe((data: AboutModel) => {
            this.about = data;
        });
    }
}

export interface IAboutViewModel {
    about: AboutModel;
}

index.html

<script src="~/lib/systemjs/dist/system.src.js"></script>
<script src="~/lib/angular2/bundles/router.js"></script>
<script src="~/lib/angular2/bundles/http.js"></script>
<script src="~/lib/angular2/bundles/angular2-polyfills.js"></script>
<script src="~/lib/angular2/bundles/angular2.dev.js"></script>
<script src="~/lib/es6-shim/es6-shim.js"></script>
<script>
    System.config({
        packages: {
            app: {
                format: 'register',
                defaultExtension: 'js'
            },
            rxjs: {
                defaultExtension: 'js'
            }
        },
        map: {
            rxjs: "lib/rxjs"
        }
    });
    System.import('app/boot')
            .then(null, console.error.bind(console));
</script>
42
Ryan Langton

次回はビューとモデルを含めてください(app/about/about.htmlおよびabout.model)。

arrayを返す場合は、 asyncPipe を使用できます。これは、「ObservableまたはPromiseにサブスクライブし、最新の新しい値が発行されると、非同期パイプは変更をチェックするコンポーネントにマークを付けます」。したがって、ビューは新しい値で更新されます。

primitive type(string、number、boolean)を返す場合は、asyncPipeも使用できます。

オブジェクトを返す場合、 私はasyncPipeを使用する方法を知りません、次のように 安全なナビゲーション演算子?.と組み合わせて非同期パイプを使用できます。

{{(objectData$ | async)?.name}}

しかし、それは少し複雑に見えるので、表示したいオブジェクトプロパティごとにそれを繰り返す必要があります。

コメントで@pixelbitsに言及されているように、コントローラーのobservableにsubscribe()して、含まれているオブジェクトをコンポーネントプロパティに保存できます。次に、テンプレートで安全なナビゲーション演算子またはNgIfを使用します。

service.ts

import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';
import 'rxjs/add/operator/map';  // we need to import this now

@Injectable()
export class MyService {
  constructor(private _http:Http) {}
  getArrayData() {
    return this._http.get('./data/array.json')
      .map(data => data.json());
  }
  getPrimitiveData() {
    return this._http.get('./data/primitive.txt')
      .map(data => data.text());   // note .text() here
  }
  getObjectData() {
    return this._http.get('./data/object.json')
      .map(data => data.json());
  }
}

app.ts

@Component({
  selector: 'my-app',
  template: `
    <div>array data using '| async':
      <div *ngFor="let item of arrayData$ | async">{{item}}</div>
    </div>
    <div>primitive data using '| async': {{primitiveData$ | async}}</div>
    <div>object data using .?: {{objectData?.name}}</div>
    <div *ngIf="objectData">object data using NgIf: {{objectData.name}}</div>`
  providers: [HTTP_PROVIDERS, MyService]
})
export class AppComponent {
  constructor(private _myService:MyService) {}
  ngOnInit() {
    this.arrayData$     = this._myService.getArrayData();
    this.primitiveData$ = this._myService.getPrimitiveData();
    this._myService.getObjectData()
      .subscribe(data => this.objectData = data);
  }
}

data/array.json

[ 1,2,3 ]

data/primitive.json

Greetings SO friends!

data/object.json

{ "name": "Mark" }

出力:

array data using '| async':
1
2
3
primitive data using '| async': Greetings SO friends!
object data using .?: Mark
object data using NgIf: Mark

Plunker

55
Mark Rajcok

ビューabout.titleabout.htmlを参照しているようですが、about変数は、httpリクエストが完了した後にのみインスタンス化されます。このエラーを回避するには、about.html<div *ngIf="about"> ... </div>でラップします

30
TheKojuEffect

前の答えは正しいです。テンプレートで使用する前に、変数が定義されているかどうかを確認する必要があります。 HTTPリクエストを使用するには、それを定義する時間が必要です。 * ngIfを使用して確認します。例はangularから https://angular.io/docs/ts/latest/tutorial/toh-pt5.html で提供され、例は http:/ /plnkr.co/edit/?p=preview

<div *ngIf="hero">
  <h2>{{hero.name}} details!</h2>
<div>

App/hero-detail.component [tsおよびhtml]を確認できます

17
Zlatko Yankov