angular 2:在 Promise 中使用组件的 `this`
angular 2: working with a component's `this` in a Promise
更新
我在控制台日志后放了一个 return
并确定问题不是 Promise —— 它读起来很好。在我在下面的代码中突出显示的行中,this.contentItems = cpl.ary as ContentItem[];
是发生错误的地方。我已经验证 cpl
是一个包含一个 属性、ary
的对象,它是一个包含 4 个项目的数组。它在控制台日志中显示正常。但是,当我尝试获取 ary
属性 as ContentItem[]
时,它会出现 null
并尝试将其分配给左侧的空 Content[]
.所以这纯粹是一个铸造或其他类型分配问题。找到答案后,我将更改此问题的主题。
我的问题是 this.contentItems
在 content-display.component.ts.
的 then
方法中被设置为 null
我正在消费的JSON是这样的:
{ "ary":[
{
"id": "1",
"name": "Test Project",
"nextDate": "2016-12-01T16:00:00",
"dateDescription": "ends",
"contentType": "project",
"link": "http://google.com",
"topics": ["1"]
},
{
"id": "3",
"name": "Dummy Article",
"nextDate": "2016-12-21T00:00:00",
"dateDescription": "expires",
"contentType": "article",
"link": "http://msn.com",
"topics": ["2"]
}
]}
在我的内容中-item.service.ts:
getContentItems(): Promise<ContentPayload> {
if( window.location.hostname === "localhost" )
return Promise.resolve( CONTENTITEMS );
else
return this.http.get( this.contentUrl )
.toPromise()
.then( (response) => { return response.json() } )
.then( (data) => { return data as ContentPayload })
.catch( this.handleError );
}
回到我的内容-display.component.ts
private contentItems: ContentItem[] = [];
getContentItems(): void {
this.contentItemService.getContentItems()
.then( (contentPayload) => this.getArrayFromPayload )
.then( this.getSelectOptions );
}
getArrayFromPayload( cpl: ContentPayload ): void {
this.contentItems = cpl["ary"];
/********** above, this.contentItems is set to null ************/
this.filteredItems = this.contentItems;
}
内容有效负载:
export class ContentPayload {
ary: any[];
}
内容项:
export class ContentItem {
id: string;
name: string;
nextDate: Date;
dateDescription: string;
contentType: string;
link: string;
isClicked?: boolean = false;
topics: string[];
}
为了完整起见,包括了这个。它做什么并不重要,错误发生在上面。
getSelectOptions( ): void {
// return TOPICMAP;
var topicMap: Topic[] = TOPICMAP;
var filteredTopicMap: Topic[] = [];
var uniqueValues: string[] = this.makeContentSet( this.getTopicIDs( this.contentItems ) );
for( var i = 0; i < uniqueValues.length; i++ ) {
filteredTopicMap.push( this.getTopicWithID( topicMap, uniqueValues[i] ) );
}
this.selectOptions = filteredTopicMap;
}
我的这个组件模板("this" 的风格和使用上的不一致最终会被清除。你知道 iPad 没有反引号吗?):
template: `
<h2>{{title}}</h2>
<div>Maximum of 20 items</div>
<div>Order:
<span [class.clicked]="latestOldest==='latest'" (click)="mySort('latest')">latest first</span>
|
<span [class.clicked]="latestOldest==='oldest'" (click)="mySort('oldest');">oldest first</span>
</div>
<div>
Filters:
<ng-select
[options] = "this.selectOptions"
[multiple] = "this.showMultiple"
placeholder = "Select topics"
[allowClear] = "true"
theme = "default"
(selected) = "onSelected( $event )"
(deselected) = "onDeselected( $event )">
</ng-select>
<ul>
<li *ngFor="let contentItem of filteredItems"
(click)="onClick(contentItem)"
class="{{contentItem.contentType}}"
[class.clicked]="contentItem.isClicked">
<h3>{{contentItem.name}}</h3>
<div>{{ contentItem.contentType | uppercase }} {{ contentItem.dateDescription}}
{{ contentItem.nextDate.toGMTString() | date:'mediumDate' }}
{{ (contentItem.contentType !== "article")? (contentItem.nextDate | date:'shortTime'): '' }}
</div>
</li>
</ul>
</div>
`
您的问题是由于没有正确处理this
造成的。这里有很多关于这个主题的问题。
当你写下以下内容时
getContentItems(): void {
this.contentItemService.getContentItems()
.then( (contentPayload) => this.getArrayFromPayload )
.then( this.getSelectOptions );
}
那么你基本上是在提取它所定义的对象的功能(从你的例子中的组件实例)。该函数仍然引用一些 this
但它不会再作为 this
与您的组件一起调用,这就是导致您出现问题的原因。
注释中提到的使用.then( this.getArrayFromPayload )
的方法也不起作用,因为这也会在调用函数时更改上下文。
有几种方法可以解决这个问题。您可以 bind()
将传递给 then
方法的函数传递给调用者上下文的 this,就像这样
.then(this.getArrayFromPayload.bind(this))
这样做会将当前使用的 this
设置为函数,以便在调用函数时保留它。 (它基本上 returns 一个具有正确 this 引用的新函数)
另一种方法是使用没有自己的 this
上下文的箭头函数,因此引用外部 this
.
getArrayFromPayload = (cpl: ContentPayload) => {
this.contentItems = cpl["ary"];
this.filteredItems = this.contentItems;
}
这样做可以让您像 .then( this.getArrayFromPayload )
一样调用它。
有关基本概念的详细信息,请参阅 JavaScript 中涵盖 this
基础知识的出色答案:How to access the correct `this` context inside a callback?
更新
我在控制台日志后放了一个 return
并确定问题不是 Promise —— 它读起来很好。在我在下面的代码中突出显示的行中,this.contentItems = cpl.ary as ContentItem[];
是发生错误的地方。我已经验证 cpl
是一个包含一个 属性、ary
的对象,它是一个包含 4 个项目的数组。它在控制台日志中显示正常。但是,当我尝试获取 ary
属性 as ContentItem[]
时,它会出现 null
并尝试将其分配给左侧的空 Content[]
.所以这纯粹是一个铸造或其他类型分配问题。找到答案后,我将更改此问题的主题。
我的问题是 this.contentItems
在 content-display.component.ts.
then
方法中被设置为 null
我正在消费的JSON是这样的:
{ "ary":[
{
"id": "1",
"name": "Test Project",
"nextDate": "2016-12-01T16:00:00",
"dateDescription": "ends",
"contentType": "project",
"link": "http://google.com",
"topics": ["1"]
},
{
"id": "3",
"name": "Dummy Article",
"nextDate": "2016-12-21T00:00:00",
"dateDescription": "expires",
"contentType": "article",
"link": "http://msn.com",
"topics": ["2"]
}
]}
在我的内容中-item.service.ts:
getContentItems(): Promise<ContentPayload> {
if( window.location.hostname === "localhost" )
return Promise.resolve( CONTENTITEMS );
else
return this.http.get( this.contentUrl )
.toPromise()
.then( (response) => { return response.json() } )
.then( (data) => { return data as ContentPayload })
.catch( this.handleError );
}
回到我的内容-display.component.ts
private contentItems: ContentItem[] = [];
getContentItems(): void {
this.contentItemService.getContentItems()
.then( (contentPayload) => this.getArrayFromPayload )
.then( this.getSelectOptions );
}
getArrayFromPayload( cpl: ContentPayload ): void {
this.contentItems = cpl["ary"];
/********** above, this.contentItems is set to null ************/
this.filteredItems = this.contentItems;
}
内容有效负载:
export class ContentPayload {
ary: any[];
}
内容项:
export class ContentItem {
id: string;
name: string;
nextDate: Date;
dateDescription: string;
contentType: string;
link: string;
isClicked?: boolean = false;
topics: string[];
}
为了完整起见,包括了这个。它做什么并不重要,错误发生在上面。
getSelectOptions( ): void {
// return TOPICMAP;
var topicMap: Topic[] = TOPICMAP;
var filteredTopicMap: Topic[] = [];
var uniqueValues: string[] = this.makeContentSet( this.getTopicIDs( this.contentItems ) );
for( var i = 0; i < uniqueValues.length; i++ ) {
filteredTopicMap.push( this.getTopicWithID( topicMap, uniqueValues[i] ) );
}
this.selectOptions = filteredTopicMap;
}
我的这个组件模板("this" 的风格和使用上的不一致最终会被清除。你知道 iPad 没有反引号吗?):
template: `
<h2>{{title}}</h2>
<div>Maximum of 20 items</div>
<div>Order:
<span [class.clicked]="latestOldest==='latest'" (click)="mySort('latest')">latest first</span>
|
<span [class.clicked]="latestOldest==='oldest'" (click)="mySort('oldest');">oldest first</span>
</div>
<div>
Filters:
<ng-select
[options] = "this.selectOptions"
[multiple] = "this.showMultiple"
placeholder = "Select topics"
[allowClear] = "true"
theme = "default"
(selected) = "onSelected( $event )"
(deselected) = "onDeselected( $event )">
</ng-select>
<ul>
<li *ngFor="let contentItem of filteredItems"
(click)="onClick(contentItem)"
class="{{contentItem.contentType}}"
[class.clicked]="contentItem.isClicked">
<h3>{{contentItem.name}}</h3>
<div>{{ contentItem.contentType | uppercase }} {{ contentItem.dateDescription}}
{{ contentItem.nextDate.toGMTString() | date:'mediumDate' }}
{{ (contentItem.contentType !== "article")? (contentItem.nextDate | date:'shortTime'): '' }}
</div>
</li>
</ul>
</div>
`
您的问题是由于没有正确处理this
造成的。这里有很多关于这个主题的问题。
当你写下以下内容时
getContentItems(): void {
this.contentItemService.getContentItems()
.then( (contentPayload) => this.getArrayFromPayload )
.then( this.getSelectOptions );
}
那么你基本上是在提取它所定义的对象的功能(从你的例子中的组件实例)。该函数仍然引用一些 this
但它不会再作为 this
与您的组件一起调用,这就是导致您出现问题的原因。
注释中提到的使用.then( this.getArrayFromPayload )
的方法也不起作用,因为这也会在调用函数时更改上下文。
有几种方法可以解决这个问题。您可以 bind()
将传递给 then
方法的函数传递给调用者上下文的 this,就像这样
.then(this.getArrayFromPayload.bind(this))
这样做会将当前使用的 this
设置为函数,以便在调用函数时保留它。 (它基本上 returns 一个具有正确 this 引用的新函数)
另一种方法是使用没有自己的 this
上下文的箭头函数,因此引用外部 this
.
getArrayFromPayload = (cpl: ContentPayload) => {
this.contentItems = cpl["ary"];
this.filteredItems = this.contentItems;
}
这样做可以让您像 .then( this.getArrayFromPayload )
一样调用它。
有关基本概念的详细信息,请参阅 JavaScript 中涵盖 this
基础知识的出色答案:How to access the correct `this` context inside a callback?