webentwicklung-frage-antwort-db.com.de

Beobachtbarer Typfehler: Die Eigenschaft von undefined kann nicht gelesen werden

In meiner Angular 2-Anwendung erhalte ich eine Fehlermeldung:

Die Eigenschaft 'title' von undefined kann nicht gelesen werden.

Dies ist eine sehr einfache Komponente, die nur versucht, ein Minimum für die Arbeit hier zu bekommen. Es trifft meinen API-Controller (merkwürdigerweise mehrmals) und es scheint den Rückruf zu treffen, nachdem ein Objekt zurückgegeben wurde. Mein console.log gibt das erwartete Objekt aus. Hier ist der vollständige Fehler:

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)

Der Dienst (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();
        });
    }
}

Die Komponente (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

Bitte geben Sie beim nächsten Mal Ihre Ansicht und Ihr Modell an (app/about/about.html und about.model).

Wenn Sie ein Array zurückgeben, können Sie die asyncPipe verwenden, die "eine Observable oder Promise abonniert und die neueste zurückgibt Wenn ein neuer Wert ausgegeben wird, markiert die asynchrone Pipe die Komponente, die auf Änderungen überprüft werden soll. "Die Ansicht wird daher mit dem neuen Wert aktualisiert.

Wenn Sie einen primitiven Typ (Zeichenfolge, Zahl, Boolescher Wert) zurückgeben, können Sie auch die asyncPipe verwenden.

Wenn Sie ein Objekt zurückgeben, Mir ist keine Möglichkeit bekannt, asyncPipe zu verwendenWir könnten die asynchrone Pipe in Verbindung mit dem Operator sichere Navigation?. wie folgt verwenden:

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

Das sieht jedoch etwas kompliziert aus, und das müssten wir für jede Objekteigenschaft wiederholen, die wir anzeigen möchten.

Wie in einem Kommentar mit @pixelbits angegeben, können Sie subscribe() auf die Beobachtbare im Controller anwenden und das enthaltene Objekt in einer Komponenteneigenschaft speichern. Verwenden Sie dann den Operator für sichere Navigation oder NgIf in der Vorlage:

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" }

Ausgabe:

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

Es sieht so aus, als hätten Sie in der Ansicht about.title Auf about.html Verwiesen, aber die Variable about wird erst instanziiert, nachdem die Anforderung http abgeschlossen wurde. Um diesen Fehler zu vermeiden, können Sie about.html Mit <div *ngIf="about"> ... </div> Umbrechen.

30
TheKojuEffect

Die vorherige Antwort ist richtig. Sie müssen überprüfen, ob die Variable definiert ist, bevor Sie sie in Ihrer Vorlage verwenden. Bei der Verwendung einer HTTP-Anforderung wird Zeit benötigt, um sie zu definieren. Verwenden Sie * ngIf, um dies zu überprüfen. Das Beispiel ist aus angular mit https://angular.io/docs/ts/latest/tutorial/toh-pt5.html und das Beispiel ist http://plnkr.co/edit/?p=preview

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

Sie können app/hero-detail.component [ts und html] überprüfen

17
Zlatko Yankov