【问题标题】:Observable type error: cannot read property of undefined可观察到的类型错误:无法读取未定义的属性
【发布时间】:2016-04-16 12:53:50
【问题描述】:

在我的 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>

【问题讨论】:

  • 绑定到 about?.title。当 about 未定义时,它更宽容

标签: angular observable rxjs


【解决方案1】:

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

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

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

如果你要返回一个对象我不知道有什么方法可以使用asyncPipe,我们可以使用异步管道,结合safe navigation operator ?.如下:

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

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

正如评论中提到的@pixelbits,您可以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);
  }
}

数据/array.json

[ 1,2,3 ]

数据/primitive.json

Greetings SO friends!

数据/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

【讨论】:

  • 马克,你在这里使用? (猫王操作员)为前端。如果您需要服务中的数据来拨打另一个电话怎么办?我有一个 service.getId().flatMap( id => return service.getUser(id);).subscribe(data => console.log(data)) 但我的数据返回 undefined 即使它已登录服务.
  • @AdamMendoza,让服务返回 flatMap,然后在组件中订阅它。还要在视图/模板中使用?,因为数据是异步解析的。如果不清楚,我建议您发布一个显示您的服务和组件的新问题。
  • 我一直在寻找和寻找,你的答案是迄今为止最清楚的!谢谢一百万老兄
  • 如果你不喜欢使用safe operator,你可以使用about对象as另一个对象——见angular-ngif-async-pipe
  • 如果你有类似*ngFor="let post of ((postsFiltered$ | async)['Original-content'])"的东西怎么办? )?['Original-content'] 不是一个有效的语法。
【解决方案2】:

看起来您在about.html 视图中引用了about.title,但about 变量仅在http 请求完成后才被实例化。为避免此错误,您可以将 about.html 包装为 &lt;div *ngIf="about"&gt; ... &lt;/div&gt;

【讨论】:

  • 我已经这样做了,但仍然得到错误。 about.html 中的第一个元素:
    也添加到 about.component.ts:import {CORE_DIRECTIVES} from 'angular2/common';指令:[CORE_DIRECTIVES],
  • 我需要在 ngInit 中添加以下行以使其正常工作: this.about = new AboutModel();似乎不太理想,那和 ngIf 解决方案。希望我可以从我的 html 模板绑定到 Observable,可以吗?
  • @RyanLangton 很高兴你成功了。无论如何,你可以试试 OnActivate angular.io/docs/js/latest/api/router/OnActivate-interface.html 。与stackoverflow.com/questions/34734367/… 相关
  • 谢谢,这正是我的问题。
【解决方案3】:

前面的答案是正确的。 在模板中使用变量之前,您需要检查变量是否已定义。使用 HTTP 请求需要时间来定义它。使用 *ngIf 进行检查。示例是从角度提供的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]

【讨论】:

    猜你喜欢
    相关资源
    最近更新 更多
    热门标签