最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Angular2: *ngFor, AsyncPipe, and index - Stack Overflow

programmeradmin6浏览0评论

In a previous question here, I am trying to show a date only when it has changed between 2 messages.

The difference is that I am now connected to a realtime database (Firebase), so I subscribe to the data source and pass it through the async pipe before going through index and *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>

This works well when I first load the view, however, if I push a new entry into chat, I get the following error:

TypeError: Cannot read property '0' of null

Maybe I am not too sure how the async pipe works, but when I try returning {{ (chat | async).length }}, it works as intended. Any suggestions on a workaround/proper practice?

In a previous question here, I am trying to show a date only when it has changed between 2 messages.

The difference is that I am now connected to a realtime database (Firebase), so I subscribe to the data source and pass it through the async pipe before going through index and *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>

This works well when I first load the view, however, if I push a new entry into chat, I get the following error:

TypeError: Cannot read property '0' of null

Maybe I am not too sure how the async pipe works, but when I try returning {{ (chat | async).length }}, it works as intended. Any suggestions on a workaround/proper practice?

Share Improve this question edited May 23, 2017 at 11:54 CommunityBot 11 silver badge asked Jun 23, 2016 at 7:38 shinglesminglesshinglesmingles 5403 gold badges6 silver badges14 bronze badges 4
  • Did you check if || is short-circuiting when used in template? Might be a bug. – Günter Zöchbauer Commented Jun 23, 2016 at 7:46
  • @GünterZöchbauer The short-circuit is intended as there's nothing before the message at index 0, so it should return true. On index 1, [i-1] evaluates to 0 and that's where the exception is thrown. – shinglesmingles Commented Jun 23, 2016 at 7:52
  • I know it's intended but I'm suspicious whether it is actually happening. – Günter Zöchbauer Commented Jun 23, 2016 at 7:54
  • @GünterZöchbauer Yep it does! I may post a picture in the OP, but the first message bubble with the date "button" is showing, nothing after that. – shinglesmingles Commented Jun 23, 2016 at 7:56
Add a comment  | 

5 Answers 5

Reset to default 15

In case someone else finds this since it's the first result with the key words, I was able to get the index of a *ngFor with the async pipe like this (assuming messages is an Observable of an iterable):

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

Still not sure how the AsyncPipe can be manipulated here, but I did mange to find a workaround that involves not using the pipe. I will await a better answer hopefully before marking this closed.

I subscribe to the data source in my class, and manipulate the array with a for loop before displaying it.

The (template) code in my question has now become:

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

And in the controller:

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

Note that I am currently using DatePipe in the class code (as well as in the template), so it is necessary to use providers: [DatePipe] along with pipes: [DatePipe].

If you want to use *ngFor, AsyncPipe, and index together - you should use this construction:

  <ul>
      <li *ngFor="let person of obsPersons | async as persons; index as i">
           {{i + 1}} out of {{persons.length}}: {{person.personId}}  
       </li>
  </ul> 

Tested and works on Angular 7.

As mentioned above by @Primal Zed let company of (filteredCompaniesAliases | async); let i = index" should do the trick, syntax sample with plain html table:

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

in your component:

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

...

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

I use a function for showing (or not) the date. So to show the date (in this example) only when it changes:

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

With showOnChange:

  showOnChange(currentDate) {
    if (currentDate != this.previousDate) {
      this.previousDate = currentDate
      return currentDate
    }
    return ''
  }
发布评论

评论列表(0)

  1. 暂无评论