Skip to content

Commit

Permalink
feat(pagination): Add pagination sample using mat-paginator
Browse files Browse the repository at this point in the history
  • Loading branch information
msari-ipe-ext-1 committed Jun 14, 2024
1 parent f8afdc0 commit d583ebe
Show file tree
Hide file tree
Showing 8 changed files with 298 additions and 83 deletions.
1 change: 1 addition & 0 deletions apps/demo/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<a mat-list-item routerLink="/flight-search">withRedux</a>
<a mat-list-item routerLink="/flight-search-data-service-simple">withDataService (Simple)</a>
<a mat-list-item routerLink="/flight-search-data-service-dynamic">withDataService (Dynamic)</a>
<a mat-list-item routerLink="/flight-search-with-pagination">withPagination</a>
<a mat-list-item routerLink="/flight-search-redux-connector">Redux Connector</a>
<a mat-list-item routerLink="/todo-storage-sync">withStorageSync</a>
</mat-nav-list>
Expand Down
9 changes: 0 additions & 9 deletions apps/demo/src/app/app.routes.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
import { Route } from '@angular/router';
import { TodoComponent } from './todo/todo.component';
import { FlightSearchComponent } from './flight-search/flight-search.component';
import { FlightSearchSimpleComponent } from './flight-search-data-service-simple/flight-search-simple.component';
import { FlightEditSimpleComponent } from './flight-search-data-service-simple/flight-edit-simple.component';
import { FlightSearchDynamicComponent } from './flight-search-data-service-dynamic/flight-search.component';
import { FlightEditDynamicComponent } from './flight-search-data-service-dynamic/flight-edit.component';
import { TodoStorageSyncComponent } from './todo-storage-sync/todo-storage-sync.component';
import { FlightSearchReducConnectorComponent } from './flight-search-redux-connector/flight-search.component';
import { provideFlightStore } from './flight-search-redux-connector/+state/redux';

export const appRoutes: Route[] = [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<h2 class="title">Flight Search (Pagination)</h2>


<form (ngSubmit)="search()">
<div>
<mat-form-field>
<mat-label>Name</mat-label>
<input [(ngModel)]="searchParams.from" name="from" matInput />
</mat-form-field>
</div>

<div>
<mat-form-field>
<mat-label>Name</mat-label>
<input [(ngModel)]="searchParams.to" name="to" matInput />
</mat-form-field>
</div>

<button mat-raised-button>Search</button>
</form>

<mat-table [dataSource]="dataSource">
<!-- From Column -->
<ng-container matColumnDef="from">
<mat-header-cell *matHeaderCellDef>From</mat-header-cell>
<mat-cell *matCellDef="let element">{{ element.from }}</mat-cell>
</ng-container>

<!-- To Column -->
<ng-container matColumnDef="to">
<mat-header-cell *matHeaderCellDef>To</mat-header-cell>
<mat-cell *matCellDef="let element">{{ element.to }}</mat-cell>
</ng-container>

<!-- Date Column -->
<ng-container matColumnDef="date">
<mat-header-cell mat-header-cell *matHeaderCellDef>Date</mat-header-cell>
<mat-cell mat-cell *matCellDef="let element">{{
element.date | date
}}</mat-cell>
</ng-container>

<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row
*matRowDef="let row; columns: displayedColumns"
(click)="selection.toggle(row)"
></mat-row>
</mat-table>
<mat-paginator [length]="flightStore.flightTotalCount()"
[pageSize]="flightStore.flightPageSize()"
[pageIndex]="flightStore.flightCurrentPage()"
[showFirstLastButtons]="true"
[pageSizeOptions]="[5, 10, 25]"
(page)="handlePageEvent($event)"
aria-label="Select page">
</mat-paginator>
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Component, effect, inject } from '@angular/core';
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { DatePipe } from '@angular/common';
import { SelectionModel } from '@angular/cdk/collections';
import { MatInputModule } from '@angular/material/input';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { FlightBookingStore } from './flight-store';
import { Flight } from '../shared/flight';
import { MatPaginatorModule, PageEvent } from '@angular/material/paginator';

@Component({
selector: 'demo-flight-search-with-pagination',
templateUrl: 'flight-search-with-pagination.component.html',
standalone: true,
imports: [
MatTableModule,
MatPaginatorModule,
DatePipe,
MatInputModule,
FormsModule,
MatButtonModule,
],
providers: [FlightBookingStore]
})
export class FlightSearchWithPaginationComponent {
searchParams: { from: string; to: string } = { from: 'Wien', to: '' };
flightStore = inject(FlightBookingStore);

displayedColumns: string[] = ['from', 'to', 'date'];
dataSource = new MatTableDataSource<Flight>([]);
selection = new SelectionModel<Flight>(true, []);

constructor() {
effect(() => {
this.dataSource.data = this.flightStore.selectedPageFlightEntities();
});
this.flightStore.loadFlightEntities();
}

search() {
this.flightStore.updateFlightFilter(
this.searchParams
);
this.flightStore.loadFlightEntities();
}

handlePageEvent(e: PageEvent) {
this.flightStore.setFlightPageSize(e.pageSize);
this.flightStore.gotoFlightPage(e.pageIndex);
}
}
26 changes: 26 additions & 0 deletions apps/demo/src/app/flight-search-with-pagination/flight-store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { FlightService } from '../shared/flight.service';

import { signalStore, type } from '@ngrx/signals';

import { withEntities } from '@ngrx/signals/entities';
import { withCallState, withDataService, withPagination } from 'ngrx-toolkit';
import { Flight } from '../shared/flight';

export const FlightBookingStore = signalStore(
withCallState({
collection: 'flight',
}),
withEntities({
entity: type<Flight>(),
collection: 'flight',
}),
withDataService({
dataServiceType: FlightService,
filter: { from: 'Wien', to: '' },
collection: 'flight',
}),
withPagination({
entity: type<Flight>(),
collection: 'flight',
})
);
5 changes: 5 additions & 0 deletions apps/demo/src/app/lazy-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { FlightEditDynamicComponent } from './flight-search-data-service-dynamic
import { TodoStorageSyncComponent } from './todo-storage-sync/todo-storage-sync.component';
import { provideFlightStore } from './flight-search-redux-connector/+state/redux';
import { FlightSearchReducConnectorComponent } from './flight-search-redux-connector/flight-search.component';
import { FlightSearchWithPaginationComponent } from './flight-search-with-pagination/flight-search-with-pagination.component';

export const lazyRoutes: Route[] = [
{ path: 'todo', component: TodoComponent },
Expand All @@ -21,6 +22,10 @@ export const lazyRoutes: Route[] = [
path: 'flight-search-data-service-dynamic',
component: FlightSearchDynamicComponent,
},
{
path: 'flight-search-with-pagination',
component: FlightSearchWithPaginationComponent
},
{ path: 'flight-edit-dynamic/:id', component: FlightEditDynamicComponent },
{ path: 'todo-storage-sync', component: TodoStorageSyncComponent },
{
Expand Down
34 changes: 14 additions & 20 deletions libs/ngrx-toolkit/src/lib/with-pagination.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { patchState, signalStore, type } from '@ngrx/signals';
import { createPageArray, gotoPage, setPageSize, withPagination } from './with-pagination';
import {
createPageArray,
gotoPage,
setPageSize,
withPagination,
} from './with-pagination';
import { setAllEntities, withEntities } from '@ngrx/signals/entities';

type Book = { id: number; title: string; author: string };
Expand All @@ -21,13 +26,13 @@ describe('withPagination', () => {
const store = new Store();

patchState(store, setAllEntities(generateBooks(55)));
expect(store.currentPage()).toBe(1);
expect(store.currentPage()).toBe(0);
expect(store.pageCount()).toBe(6);
}),
it('should use and update a pagination with collection', () => {
const Store = signalStore(
withEntities({ entity: type<Book>(), collection: 'books' }),
withPagination({ collection: 'books' })
withPagination({ entity: type<Book>(), collection: 'books' })
);

const store = new Store();
Expand All @@ -37,41 +42,30 @@ describe('withPagination', () => {
setAllEntities(generateBooks(55), { collection: 'books' })
);

patchState(store, gotoPage(6, { collection: 'books' }));
expect(store.booksCurrentPage()).toBe(6);
patchState(store, gotoPage(5, { collection: 'books' }));
expect(store.booksCurrentPage()).toBe(5);
expect(store.selectedPageBooksEntities().length).toBe(5);
expect(store.booksPageCount()).toBe(6);
}),
it('should react on enitiy changes', () => {
const Store = signalStore(
withEntities({ entity: type<Book>()}),
withEntities({ entity: type<Book>() }),
withPagination()
);

const store = new Store();

patchState(
store,
setAllEntities(generateBooks(100))
);
patchState(store, setAllEntities(generateBooks(100)));

expect(store.pageCount()).toBe(10);

patchState(
store,
setAllEntities(generateBooks(20))
);
patchState(store, setAllEntities(generateBooks(20)));

expect(store.pageCount()).toBe(2);


patchState(
store,
setPageSize(5)
);
patchState(store, setPageSize(5));

expect(store.pageCount()).toBe(4);

}),
describe('internal pageNavigationArray', () => {
it('should return an array of page numbers', () => {
Expand Down
Loading

0 comments on commit d583ebe

Please sign in to comment.