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?