As I delve deeper into working reactively with Angular 15 and RxJS observables for a UI component, my focus lies on subscribing to data solely within the component template (html). The data is fetched from an external system through a service. However, a crucial challenge I face is that the received data spans multiple days and requires segmentation for display purposes.
The displayed data consists of individual components representing rows retrieved from the service call, which involves making an HTTP request to an external host.
this.Entries$ = this.Http_.get<Array<IEntry>>('http://host.com/api/entry');
The data obtained is structured as an array of records containing information such as EntryDate, UserId, Description, TimeWorked, etc. The external API sends these records back as a flat array in no particular order, necessitating potential sorting for processing requirements.
[
{ "EnterDate": 20221025, "UserId": "JohnDoe", "TimeWorked": 2.5, ... },
{ "EnterDate": 20221025, "UserId": "JohnDoe", "TimeWorked": 4.5, ... },
{ "EnterDate": 20221025, "UserId": "BSmith", "TimeWorked": 5, ... },
{ "EnterDate": 20221026, "UserId": "JohnDoe", "TimeWorked": 4, ... },
{ "EnterDate": 20221026, "UserId": "BSmith", "TimeWorked": 5, ... },
{ "EnterDate": 20221026, "UserId": "JohnDoe", "TimeWorked": 2, ... },
]
My current HTML template iterates over the Entries$ observable assuming it's meant for a single day only.
<ng-container *ngFor="let OneEntry of (Entries$ | async)">
<one-entry-component [data]=OneEntry />
</ng-container>
I aim to segregate the array of records into distinct datasets based on their EntryDate (and optionally user), akin to using groupBy(), but accessing internal record references within the groupBy() function remains unclear to me.
Once segmented, I envision having multiple one-day-components on the page, each containing the one-entry-component within them.
|---------------------------------------------------------------|
| |
| |-One Day 1-------------###-| |-One Day 2-------------###-| |
| | | | | |
| | [ One Line ] | | [ One Line ] | |
| | [ One Line ] | | [ One Line ] | |
| | [ One Line ] | | [ One Line ] | |
| | [ One Line ] | | [ One Line ] | |
| | | | | |
| |---------------------------| |---------------------------| |
| |
| |-One Day 3-------------###-| |-One Day 4-------------###-| |
| | | | | |
| | [ One Line ] | | [ One Line ] | |
| | [ One Line ] | | [ One Line ] | |
| | [ One Line ] | | [ One Line ] | |
| | [...
These separate boxes represent unique days accounted for in the response. Should there be two different dates, the display would adjust accordingly, accommodating varying numbers of dates.
To achieve this, I require an Observable featuring the necessary dates for segmentation (and possibly users) to pass as data to the
<one-day-component [data]=OneDateOneUser$ />
. This data aids in tallying time entries for the title, a task simplified through a .pipe(map()) operation.
Within the one-day-component, I would then iterate through the OneDateOneUser$ observable to extract individual records to convey to the one-entry-component, mirroring existing functionality.
While researching how to achieve this, the RxJS groupBy operator seems promising. Despite its potential benefits, navigating and manipulating the inner array data poses a learning curve given my novice status with RxJS.
The example provided uses individual records and not an array, showcasing the efficacy of the RxJS reference when applied appropriately.
import { of, groupBy, mergeMap, reduce, map } from 'rxjs';
of(
{ id: 1, name: 'JavaScript' },
{ id: 2, name: 'Parcel' },
{ id: 2, name: 'webpack' },
{ id: 1, name: 'TypeScript' },
{ id: 3, name: 'TSLint' }
).pipe(
groupBy(p => p.id, { element: p => p.name }),
mergeMap(group$ => group$.pipe(reduce((acc, cur) => [...acc, cur], [`${ group$.key }`]))),
map(arr => ({ id: parseInt(arr[0], 10), values: arr.slice(1) }))
)
.subscribe(p => console.log(p));
// displays:
// { id: 1, values: [ 'JavaScript', 'TypeScript' ] }
// { id: 2, values: [ 'Parcel', 'webpack' ] }
// { id: 3, values: [ 'TSLint' ] }
However, altering the data structure in the example by converting it to an array similar to how my data is returned results in issues, emphasizing the need for further exploration and understanding to address the problem effectively:
import { of, groupBy, mergeMap, reduce, map } from 'rxjs';
of(
[
{ id: 1, name: 'JavaScript' },
{ id: 2, name: 'Parcel' },
{ id: 2, name: 'webpack' },
{ id: 1, name: 'TypeScript' },
{ id: 3, name: 'TSLint' }
]
).pipe(
groupBy(p => p.id, { element: p => p.name }),
mergeMap(group$ => group$.pipe(reduce((acc, cur) => [...acc, cur], [`${ group$.key }`]))),
map(arr => ({ id: parseInt(arr[0], 10), values: arr.slice(1) }))
)
.subscribe(p => console.log(p));