Angular2: *ngFor, AsyncPipe, and index

5
在之前的一个问题中 这里,我正在尝试只在两个信息之间日期发生变化时显示该日期。
不同的是,我现在连接到实时数据库(Firebase),因此我订阅数据源并通过async管道传递它,在经过索引和*ngIf之前:
<div *ngFor='let message of (chat | async) ; let i = index'>
    <button *ngIf="i == 0 || (message.timestamp | date: 'ddMMMMyyyy') != ((chat | async)[i-1].timestamp | date: 'ddMMMMyyyy')">
        {{ message.timestamp | date:'ddMMM' }}
    </button>
    ...
    <!--More html elements below (omitted)-->
    ...
</div>

当我首次加载视图时,这个功能运行得很好,但是如果我将一个新条目推入chat,我会收到以下错误:

TypeError: Cannot read property '0' of null

也许我不太确定异步管道的工作原理,但是当我尝试返回{{ (chat | async).length }}时,它按预期工作。有关解决方法/适当的做法的任何建议吗?


你有检查过在模板中使用 || 是否会短路吗?可能是一个 bug。 - Günter Zöchbauer
@GünterZöchbauer 短路是有意的,因为在索引0之前没有任何消息,所以应该返回true。在索引1上,[i-1]计算为0,这就是抛出异常的地方。 - shinglesmingles
我知道这是有意的,但我怀疑它是否真的发生了。 - Günter Zöchbauer
@GünterZöchbauer 是的,没错!我可能会在原帖中发布一张图片,但是只显示了带有日期“按钮”的第一个消息气泡,之后没有任何内容。 - shinglesmingles
5个回答

15

假设有其他人用关键词搜索到这篇文章,我想分享一下如何在使用async pipe时获取*ngFor的索引(假设messages是一个可迭代的Observable):

<div *ngFor="let message of (messages | async); let i = index;">

3

我仍然不确定如何在这里使用AsyncPipe,但我找到了一个解决方法,其中不使用该管道。希望在关闭之前能找到更好的答案。

我在我的类中订阅数据源,并在显示之前使用for循环来操作数组。

现在我的问题(模板)代码变成了:

<div *ngFor='let message of messages; let i = index'>
    <button *ngIf="message.isNewDay">
      {{ message.timestamp | date:'ddMMM' }}
    </button>
    ...
    <!--More html elements below (omitted)-->
    ...
</div>

在控制器中:

private chatid: string //chat id passed from previous screen
private chat: FirebaseListObservable<any>;
private messages: any = [];

constructor(private firebase: FirebaseService,
            private _datepipe: DatePipe) {
}

ionViewLoaded() {
    this.chat = this.firebase.database.list('/chat-messages/' + this.chatid);
    this.chat.subscribe((data) => {
        this.messages = data;
        for (let i: number = 0; i < this.messages.length; i++) {
            if (i === 0)
                    this.messages[i].isNewDay = true;
            else {
                let date1 = this._datepipe.transform(this.messages[i].timestamp, 'ddMMMMyyyy');
                let date2 = this._datepipe.transform(this.messages[i-1].timestamp, 'ddMMMMyyyy');
                if (date1 !== date2)
                    this.messages[i].isNewDay = true;
                else
                    this.messages[i].isNewDay = false;
            }
        }
    });
}

请注意,我目前在类代码(以及模板)中使用DatePipe,因此需要同时使用providers:[DatePipe]pipes:[DatePipe]


3
如果您想同时使用 *ngFor、AsyncPipe 和 index,请使用以下结构:
  <ul>
      <li *ngFor="let person of obsPersons | async as persons; index as i">
           {{i + 1}} out of {{persons.length}}: {{person.personId}}  
       </li>
  </ul> 

已在 Angular 7 上测试并可正常工作。


2

正如 @Primal Zed 所提到的那样,let company of (filteredCompaniesAliases | async); let i = index 可以实现此目的。以下是带有普通 html 表格的语法示例:

<table>
   <tr *ngFor="let company of (filteredCompaniesAliases | async); let i = index"">
                <td>
                    {{selectedDiscountableCharge.name}}
                </td>
                <td>
                    ${{selectedDiscountableCharge.amount}}
                </td>
                <td>
                    ${{selectedDiscountableCharge.amount - ( (selectedDiscountableChargePercentanges / 100) * selectedDiscountableCharge.amount ) }}
                </td>
    </tr>
</table>

在你的组件中:

companyAliasFilterCtrl: FormControl;
filteredCompaniesAliases: Observable<any[]>;

...

this.filteredCompaniesAliases = this.companyAliasFilterCtrl.valueChanges
.pipe(
  startWith(''),
  map(company => company ? this.filterOperatorsAliases(company) : this.companies.slice())
);

0
我使用一个函数来显示(或不显示)日期。因此,在这个例子中,只有当日期发生变化时才显示日期:
<ion-list no-lines>
<ion-item-sliding *ngFor="let trip of tripsExt$ | async; let i = index">
  <ion-item>
    <ion-row (click)="change(trip)">
      <ion-col col-6>
        {{showOnChange(trip.date)}}
      </ion-col>
      <ion-col col-6>
        {{trip.end_address_name}}
      </ion-col>
    </ion-row>
  </ion-item>
  <ion-item-options side="left">
    <button ion-button color="primary" (click)="delete(trip)">
      Delete
    </button>
  </ion-item-options>
</ion-item-sliding>
</ion-list>

使用 showOnChange:

  showOnChange(currentDate) {
    if (currentDate != this.previousDate) {
      this.previousDate = currentDate
      return currentDate
    }
    return ''
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接