Home > database >  History sorted by day - Angular
History sorted by day - Angular

Time:06-29

I am trying to create a history sorted by day just like the one in chrome :

Chrome History

However I am finding difficulty sorting all the histories by day.

This is what I have got so far :

History Vue

As you can see I can display the date of creation of the history element but I cannot order all the histories by the day of their creation.

This is what I have done so far :

I have created a json server from which I fetch and manipulate my data :

{
  "history": [
    {
      "id": 576791835,
      "date": "2022-06-27T18:31:38.083Z",
      
    },
    {
      "id": 911123993,
      "date": "2022-06-27T18:31:46.972Z",
    },
    {
      "id": 416865594,
      "date": "2022-06-27T18:32:54.990Z",
    },
    {
      "id": 520724162,
      "date": "2022-06-27T18:33:35.822Z",
    },
    {
      "id": 536654574,
      "date": "2022-06-27T18:33:41.821Z",
    },

  ]
}

I have implemented a store using NgRx, and this is my component :

export class HistoryComponent implements OnInit{

  Contents$:Observable<History[]>;
  ContentsInOrder$:Observable<Content[]>;
  Histories$=this.facade.Histories$;
  Date= new Date;
  

  showTrash = false;

  
  onDeleteHistory(history:History){
    this.facade.deleteHistory(history);
  }

  onAddHistory(content:Data){
  
    const history : History ={
      id : Math.floor(Math.random() * 999999999),
      date : new Date(),
    }
    this.store.addHistory(histo);

  @Output() click: EventEmitter<void> = new EventEmitter();

  onclick(){
    this.click.emit();
  }

  openDialog(){
    this.dialog.open(DialogComponent);
  }
}

This is the template of my component :

            <div mat-subheader *ngIf="(Histories$ | async).length===0">The documents you visit will show here</div>
  
            <mat-list-item  *ngFor="let history of Histories$ | async"  (mouseover)="history.showTrash=true" (mouseout)="history.showTrash=false" (click)="onclick()">

                <div >
                <div matList>{{history.id}}</div>     
        </div>
            </mat-list-item>
        
        </div> 
       
</mat-tab>
  </mat-tab-group>
</div>
</div>
</div>

And this is my service function:

  addHistory(history:History){
    return this.http.post(this.apiURL, history);
  }

  deleteHistory(id:number){
    return this.http.delete(this.apiURL '/' id)
  }

Thank you for your time !

CodePudding user response:

Good thing you're using observables. Looks like you've already sorted History$ items in order. So let's add one more custom pipe to group the items by day in template. For this we loop items in one layer higher using <ng-container>. We create <div> for each day, in each we place <mat-list-item> - you could change this up of course but that's the basic idea of it.

Using | datePipe we format the date history.date | date : 'EEEE, MMMM M, YYYY'. https://angular.io/api/common/DatePipe. And then to group by day I compare them using | slice with the previous value (so that each day header would be rendered once).

<strong *ngIf="(history.date | date: 'EEEE, MMMM d, YYYY') != ((Histories$ | async)[i-1]?.date | date: 'EEEE, MMMM d, YYYY')">{{ history.date | date: 'EEEE, MMMM d, YYYY' }}</strong>```

I'm sure there better ways for this, like grouping the data by key/values where key is one day, but for not it works.

However if you want dates spelled e.g "Yesterday" you need to make a custom implementation for this or use some `humanizer` package. 

```html
<ng-container *ngIf="Histories$ | async">
  <ng-container *ngFor="let history of Histories$ | async; let i = index">
    <strong
      *ngIf="
        (history.date | date: 'EEEE, MMMM d, YYYY') !=
        ((Histories$ | async)[i - 1]?.date | date: 'EEEE, MMMM d, YYYY')
      "
    >
      <br />
      <br />
      {{ history.date | date: 'EEEE, MMMM d, YYYY' }}
      <br />
    </strong>

    <br />

    <div
      *ngIf="
        (history.date | date: 'YYYY-MM-dd') ==
        (history.date | slice: 0:10).toString()
      "
    >
      <mat-list-item
        
        (mouseover)="history.showTrash = true"
        (mouseout)="history.showTrash = false"
        (click)="onclick()"
      >
        <div >
          <div matList>{{ history.date }}</div>
          <div matList>{{ history.SNS }}</div>
          <div matList>{{ history.title }}</div>
          <div matList>{{ history.DMC }}</div>
        </div>
      </mat-list-item>
    </div>
  </ng-container>
</ng-container>

Working example: https://stackblitz.com/edit/kingsbury-angular-w-material-tpuwdy?file=src/app/app.component.html

Actually on second thought - the above ⬆️ looks bad.

Segregating the data with a custom pipe beforehand is better a better idea. So instead I create a custom pipe which groups items by day and then I iterate using *ngFor over the groups and then over the items in the group:

@Pipe({ name: 'groupByDay' })
export class GroupByDayPipe implements PipeTransform {
  transform(items: any[]): any[] {
    // this gives an object with dates as keys
    const groups = items.reduce((groups, game) => {
      const date = game.date.split('T')[0];
      if (!groups[date]) {
        groups[date] = [];
      }
      groups[date].push(game);
      return groups;
    }, {});

    // Edit: to add it in the array format instead
    const groupArrays = Object.entries(groups);

    console.log(groupArrays);
    return groupArrays;
  }
}

// [['2022-06-28', Array(1)],
// ['2022-06-27', Array(7)]]
<ng-container *ngIf="Histories$ | async">
  <ng-container
    *ngFor="let group of Histories$ | async | groupByDay"
  >
    <strong>{{ group[0] | date: 'EEEE, MMMM d, YYYY' }}</strong>
    <br />
    <br />

    <div *ngFor="let history of group[1]">
      <mat-list-item
        
        (mouseover)="history.showTrash = true"
        (mouseout)="history.showTrash = false"
        (click)="onclick()"
      >
        <div >
          <div matList>{{ history.date }}</div>
          <div matList>{{ history.SNS }}</div>
          <div matList>{{ history.title }}</div>
          <div matList>{{ history.DMC }}</div>
        </div>
      </mat-list-item>
      <br />
      <br />
    </div>
  </ng-container>
</ng-container>

Working example: https://stackblitz.com/edit/kingsbury-angular-w-material-p4h1vg?file=src/app/app.component.html


Just some sidenotes. Usually we use camelCase for naming variables.

  • Related