angular 2: работа с this компонента в обещании

ОБНОВЛЕНИЕ Я поставил return после журнала консоли и определил, что проблема не в Обещании — он читается нормально. В строке, которую я выделяю в приведенном ниже коде, this.contentItems = cpl.ary as ContentItem[]; возникает ошибка. Я проверил, что cpl — это объект с одним свойством, ary, которое представляет собой массив из 4 элементов. В журнале консоли отображается нормально. Но когда я пытаюсь получить свойство ary as ContentItem[], он выдает null и пытается присвоить его моему пустому Content[] слева. Так что это чисто кастинг или другая проблема назначения типа. Я изменю тему этого вопроса, как только найду ответ.

Моя проблема в том, что this.contentItems получает значение null в методах then в content-display.component.ts.

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;
}

И мой шаблон для этого компонента (несоответствия в стиле и использовании «этого» со временем будут устранены. А вы знали, что на 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>
`

person Colin Mac    schedule 21.10.2016    source источник
comment
Я попытался смоделировать вызов http с помощью фиктивного объекта ContentPayload, но получил тот же результат.   -  person Colin Mac    schedule 21.10.2016
comment
попробуйте изменить `.then((contentPayload) =›this.getArrayFromPayload)` на `.then(this.getArrayFromPayload)`   -  person bassxzero    schedule 21.10.2016
comment
спасибо, но все еще получаю «ИСКЛЮЧЕНИЕ: не пойманный (в обещании): TypeError: невозможно установить свойство contentItems из null»   -  person Colin Mac    schedule 21.10.2016
comment
stackoverflow.com/questions/36492169/ Похоже, что шаблон для вашего компонента может быть недопустимым html. Показать шаблон?   -  person bassxzero    schedule 22.10.2016
comment
Вам не хватает закрывающего </div> для вашего открытого div в строке 9   -  person bassxzero    schedule 24.10.2016
comment
Спасибо, bassxzero, я это исправил, но это не помогло. Смотрите мое обновление в верхней части сообщения, похоже, это ошибка в том, как я обрабатываю полезную нагрузку. cpl отображается как объект JSON, но cpl.ary не находит массив ContentItem[]. Консоль, однако, показывает, что это значение [Object, Object, Object, Object], что должно быть правильным.   -  person Colin Mac    schedule 24.10.2016
comment
Да, я не думаю, что return data as ContentPayload делает то, что вы хотите. Попробуйте явно создать и настроить свойства ContentPayLoad вместо того, чтобы пытаться преобразовать объект JSON.   -  person bassxzero    schedule 24.10.2016
comment
Я сделал, как вы предложили, но я думаю, что проблема в этом конкретном случае заключалась в том, что this в функции, которая вызывалась в промисе then, не была эквивалентна компоненту. Не уверен, что это означало, но что-то другое. Поэтому я изменил .then( (contentPayload) => this.getArrayFromPayload ), который присваивал this.contentItems внутри этой функции, на .then((contentPayload)=>this.contentItems = this.getArrayFromPayload), и это сработало.   -  person Colin Mac    schedule 24.10.2016


Ответы (1)


Ваша проблема вызвана неправильной обработкой 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 устанавливается в функцию таким образом, что он сохраняется при вызове функции. (В основном он возвращает новую функцию, которая имеет правильную ссылку this)

Другим способом было бы использование стрелочной функции, которая не имеет собственного контекста this и поэтому ссылается на внешний this.

getArrayFromPayload = (cpl: ContentPayload) => {
  this.contentItems = cpl["ary"];
  this.filteredItems = this.contentItems;
}

Это позволяет вам называть его так же, как .then( this.getArrayFromPayload ).

Подробнее об основных концепциях см. в этом замечательном ответе, охватывающем основы this в JavaScript: Как получить доступ к правильному контексту this внутри обратного вызова?

person Andreas Jägle    schedule 24.10.2016