Observable type error: cannot read property of undefined
Observable type error: cannot read property of undefined
在我的 Angular 2 应用程序中,出现错误:
Cannot read property 'title' of undefined.
这是一个非常简单的组件,只是试图让最低限度的组件在这里工作。它击中了我的 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.html
中引用了 about.title
,但 about
变量仅在 http
请求完成后才实例化。为避免此错误,您可以将 about.html
换成 <div *ngIf="about"> ... </div>
下次请附上您的观点和模型(app/about/about。html 和 about.model)。
如果您要返回 数组,您可以使用 asyncPipe,"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" 因此视图将更新为新值。
如果要返回 原始类型(字符串、数字、布尔值),您还可以使用 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);
}
}
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
前面的回答是正确的。
在模板中使用变量之前,您需要检查变量是否已定义。使用 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 and html]
在我的 Angular 2 应用程序中,出现错误:
Cannot read property 'title' of undefined.
这是一个非常简单的组件,只是试图让最低限度的组件在这里工作。它击中了我的 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.html
中引用了 about.title
,但 about
变量仅在 http
请求完成后才实例化。为避免此错误,您可以将 about.html
换成 <div *ngIf="about"> ... </div>
下次请附上您的观点和模型(app/about/about。html 和 about.model)。
如果您要返回 数组,您可以使用 asyncPipe,"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" 因此视图将更新为新值。
如果要返回 原始类型(字符串、数字、布尔值),您还可以使用 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);
}
}
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
前面的回答是正确的。 在模板中使用变量之前,您需要检查变量是否已定义。使用 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 and html]