可观察类型错误:无法读取未定义的属性 [英] Observable type error: cannot read property of undefined

查看:35
本文介绍了可观察类型错误:无法读取未定义的属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的 Angular 2 应用程序中,我收到一个错误:

<块引用>

无法读取未定义的属性title".

这是一个非常简单的组件,只是试图在这里工作的最低限度.它击中了我的 API 控制器(奇怪的是多次),并且在返回对象后似乎击中了回调.我的 console.log 输出我期望的对象.这是完整的错误:

TypeError: 无法读取未定义的属性title"在 AbstractChangeDetector.ChangeDetector_About_0.detectChangesInRecordsInternal(在 <匿名> (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:10897:14), <匿名>:31:26 处评估)在 AbstractChangeDetector.detectChangesInRecords (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8824:14)在 AbstractChangeDetector.runDetectChanges (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8807:12)在 AbstractChangeDetector._detectChangesInViewChildren (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8877:14)在 AbstractChangeDetector.runDetectChanges (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8811:12)在 AbstractChangeDetector._detectChangesContentChildren (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8871:14)在 AbstractChangeDetector.runDetectChanges (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8808:12)在 AbstractChangeDetector._detectChangesInViewChildren (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8877:14)在 AbstractChangeDetector.runDetectChanges (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8811:12)在 AbstractChangeDetector.detectChanges (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8796:12)

服务(about.service.ts):

从'angular2/http'导入{Http};从'angular2/core'导入{Injectable};从'./about.model'导入{AboutModel};导入 'rxjs/add/operator/map';@Injectable()导出类 AboutService {构造函数(私有_http:Http){}得到() {返回 this._http.get('/api/about').map(res => {控制台日志(res.json());//我得到了上面那一行的错误,但这段代码仍然被命中.返回 <AboutModel>res.json();});}}

组件(about.component.ts):

import {Component, View, OnInit} from 'angular2/core';从'./about.model'导入{AboutModel};从./about.service"导入{AboutService};从 'angular2/http' 导入 {HTTP_PROVIDERS};@成分({选择器:'关于',提供者:[HTTP_PROVIDERS,AboutService],templateUrl: 'app/about/about.html'})导出类 About 实现了 IAboutViewModel、OnInit {公开关于:关于模型;构造函数(私有_aboutService:AboutService){}ngOnInit() {this._aboutService.get().subscribe((data: AboutModel) => {this.about = 数据;});}}导出接口 IAboutViewModel {关于:关于模型;}

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><脚本>系统配置({包:{应用程序: {格式:'注册',默认扩展名:'js'},rxjs:{默认扩展名:'js'}},地图: {rxjs: "lib/rxjs"}});System.import('应用程序/启动').then(null, console.error.bind(console));

解决方案

下次请包括您的视图和模型(app/about/about.html 和 about.model).

如果您返回一个数组,您可以使用asyncPipe,它订阅 Observable 或 Promise 并返回它发出的最新值.当发出新值时,异步管道标记要检查更改的组件"因此视图将使用新值更新.

如果您要返回原始类型(字符串、数字、布尔值),您还可以使用 asyncPipe.

如果您要返回一个对象我不知道有什么方法可以使用 asyncPipe,我们可以将异步管道与 安全导航运算符 ?. 如下:

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

但这看起来有点复杂,我们必须为我们想要显示的每个对象属性重复这一点.

正如评论中提到的@pixelbits,您可以subscribe() 到控制器中的可观察对象并将包含的对象存储到组件属性中.然后在模板中使用安全导航操作符或 NgIf:

service.ts

import {Injectable} from 'angular2/core';从 'angular2/http' 导入 {Http};导入 'rxjs/add/operator/map';//我们现在需要导入它@Injectable()导出类 MyService {构造函数(私有 _http:Http){}获取数组数据(){返回 this._http.get('./data/array.json').map(data => data.json());}getPrimitiveData() {返回 this._http.get('./data/primitive.txt').map(data => data.text());//注意这里的 .text()}获取对象数据(){返回 this._http.get('./data/object.json').map(data => data.json());}}

app.ts

@Component({选择器:'我的应用',模板:`<div>数组数据使用'|异步':<div *ngFor="let item of arrayData$ | async">{{item}}</div>

<div>原始数据使用'|异步':{{primitiveData$ |异步}}</div><div>对象数据使用 .?: {{objectData?.name}}</div><div *ngIf="objectData">使用 NgIf 的对象数据:{{objectData.name}}</div>`提供者:[HTTP_PROVIDERS, MyService]})导出类 AppComponent {构造函数(私有_myService:MyService){}ngOnInit() {this.arrayData$ = this._myService.getArrayData();this.primitiveData$ = this._myService.getPrimitiveData();this._myService.getObjectData().subscribe(data => this.objectData = data);}}

数据/array.json

[ 1,2,3 ]

data/primitive.json

问候朋友们!

数据/object.json

{ "name": "Mark" }

输出:

使用'|的数组数据异步':123使用'|的原始数据async':问候朋友!使用 .? 的对象数据:标记使用 NgIf 的对象数据:Mark

Plunker

In my Angular 2 application, I get an error:

Cannot read property 'title' of undefined.

This is a very simple component, just trying to get a bare minimum to work here. It hits my API controller (curiously multiple times), and it appears to hit the callback after an object is returned. My console.log outputs the object I would expect. Here is the full error:

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)

The service (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();
        });
    }
}

The Component (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>

解决方案

Please include your view and model next time (app/about/about.html and about.model).

If you are returning an array, you can use the asyncPipe, which "subscribes to an Observable or Promise and returns the latest value it has emitted. When a new value is emitted, the async pipe marks the component to be checked for changes" hence the view will be updated with the new value.

If you are returning a primitive type (string, number, boolean) you can also use the asyncPipe.

If you are returning an object, I'm not aware of any way to use asyncPipe, we could use the async pipe, in conjunction with the safe navigation operator ?. as follows:

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

But that looks a bit complicated, and we'd have to repeat that for each object property we wanted to display.

As @pixelbits mentioned in a comment, you can subscribe() to the observable in the controller and store the contained object into a component property. Then use the safe navigation operator or NgIf in the template:

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

Output:

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

Plunker

这篇关于可观察类型错误:无法读取未定义的属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆