【问题标题】:angular 2: working with a component's `this` in a Promise角度 2:在 Promise 中使用组件的 `this`
【发布时间】:2017-03-04 06:01:26
【问题描述】:

更新 我在控制台日志之后添加了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"]
  }
]}

在我的 content-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 );
}

然后回到我的 content-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>
`

【问题讨论】:

  • 我尝试使用模拟 ContentPayload 对象模拟 http 调用,但得到相同的结果。
  • 尝试将 `.then( (contentPayload) => this.getArrayFromPayload )` 更改为 ` .then( this.getArrayFromPayload )`
  • 谢谢,但仍然得到 'EXCEPTION: Uncaught (in promise): TypeError: Cannot set property 'contentItems' of null'
  • stackoverflow.com/questions/36492169/… 看起来您的组件的模板可能不是有效的 html。显示模板?
  • 第 9 行打开的 div 缺少一个结束 &lt;/div&gt;

标签: angular typescript angular2-services


【解决方案1】:

您的问题是由于未正确处理this 而引起的。这里有很多关于这个主题的问题。

当你写以下内容时

getContentItems(): void {
  this.contentItemService.getContentItems()
    .then( (contentPayload) => this.getArrayFromPayload )
    .then( this.getSelectOptions );
}

那么您基本上是在提取定义它的对象的功能(在您的情况下从组件实例中提取)。该函数仍然引用了一些this,但它不会再被您的组件调用为this,这就是导致您的问题的原因。

cmets 中提到的使用.then( this.getArrayFromPayload ) 的方式也不起作用,因为这也会在调用函数时改变上下文。

有几种方法可以解决此问题。您可以将bind() 传递给then 方法的函数传递给调用者上下文的this,就像这样

.then(this.getArrayFromPayload.bind(this))

这样做会将当前使用的this 设置为函数,以便在调用函数时保留它。 (它基本上返回一个具有正确 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?

【讨论】:

    猜你喜欢
    • 2017-04-12
    • 2016-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多