Ionic v3: Group list by date/day

2020-07-24 04:53发布

问题:

In good old version 1 of Ionic I was able to build an event-list grouped by date like this:

<ion-list ng-repeat="(key, value) in events| groupBy: 'event.date'">

  <div class="item item-divider" ng-if="value">
    {{ ::key | event.date }}
  </div>

  <ion-item class="item" ng-repeat="event in value track by event.event.event_id">
    {{ ::event.event.title }}
  </ionic-item>

</ion-list> 

While the events object looks like this (event #1 and #3 share the same date):

{
  "events": [
    {
      "id": 1,
      "date": "2017-12-26",
      "title": "First event"
    },
    {
      "id": 2,
      "date": "2017-12-30",
      "title": "Second event"
    },
    {
      "id": 3,
      "date": "2017-12-26",
      "title": "Third event"
    },
    {
      "id": 4,
      "date": "2017-12-31",
      "title": "Last event"
    }
  ]
}

This gave me a list of events stored in the "event" object grouped by "event.date". So all events on the same date where grouped by an item-divider:

+--------------+
+ 2017-12-26   +
+--------------+
| First event  |
| Third event  |
+--------------+
+ 2017-12-26   +
+--------------+
| Second event |
+--------------+
+ 2017-12-26   |
+--------------+
| Last event   |
+--------------+

How to achieve this with Ionic v3? Any ideas?

回答1:

You need a pipe to transform your data into a structure you can easily use in a template: 1 array of objects, containing other arrays of objects. Your final data should look like this:

const events = [{
  date: '2017-12-26',
  events: [{
    id: 1,
    title: 'First event'
  }, {
    id: 3,
    title: 'Third event'
  }]
}, {
  date: '2017-12-30',
  events: [{
    id: 2,
    title: 'Second event'
  }]
}, {
  date: '2017-12-31',
  events: [{
    id: 4,
    title: 'Last event'
  }]
}];

Here is my attempt at a pipe that accomplishes that:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'groupBy',
})
export class GroupByPipe implements PipeTransform {
  transform(value: any, groupByKey: string) {
    const events: any[] = [];
    const groupedElements: any = {};

    value.forEach((obj: any) => {
      if (!(obj[groupByKey] in groupedElements)) {
        groupedElements[obj[groupByKey]] = [];
      }
      groupedElements[obj[groupByKey]].push(obj);
    });

    for (let prop in groupedElements) {
      if (groupedElements.hasOwnProperty(prop)) {
        events.push({
          key: prop,
          list: groupedElements[prop]
        });
      }
    }

    return events;
  }
}

I'm sure there are better, cooler ways to do this (like in one single line with some ES6 awesomeness) but this works for now.

Now for the template, like you would in Ionic 1, you basically still have 2 loops, the first one uses the pipe to transform your data and the second (inner) loop.

Here are two versions, the second shows how the pipe can be used to group with different keys and assumes the data contains a category key in each element of the original events array:

By Date

<ion-item-group *ngFor="let group of events | groupBy: 'date'">
    <ion-item-divider color="light">
        {{ group.key }}
    </ion-item-divider>
    <ion-item *ngFor="let event of group.list">{{ event.title }}</ion-item>
</ion-item-group>

By Category

<ion-item-group *ngFor="let group of events | groupBy: 'category'">
    <ion-item-divider color="light">
        {{ group.key }}
    </ion-item-divider>
    <ion-item *ngFor="let event of group.list">{{ event.title }} {{ event.date }}</ion-item>
</ion-item-group>

You can use whatever components you wish in your template, this is straight from the Ionic Documentation.

Don't forget to import the pipe in the page where you use it. If you used lazy loading, you need to add it to the imports of the page's module.