I am trying to create a history sorted by day just like the one in chrome :
However I am finding difficulty sorting all the histories by day.
This is what I have got so far :
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.