-
Notifications
You must be signed in to change notification settings - Fork 27
Feature/data provider strategies #585
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 6 commits
b44d38c
abdd742
5896dad
1366258
2d6513e
7e88f63
862926b
8e435ac
5c848a8
5410eca
4b77842
99e1bb8
ac56e85
ab5c721
ca9c107
5e06bc4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,9 @@ | ||
| import { ChangeDetectionStrategy, Component } from '@angular/core'; | ||
| import { GridModel } from 'ng2-qgrid'; | ||
| import { Observable } from 'rxjs'; | ||
| import { map, switchMap, tap } from 'rxjs/operators'; | ||
| import { | ||
| CacheAlreadyRequestedPageStrategy, DataProvider, DataProviderServer, DataProviderStrategy, Grid, GridModel, RequestTotalCountOnceStategy | ||
| } from 'ng2-qgrid'; | ||
| import { Observable, of } from 'rxjs'; | ||
| import { map } from 'rxjs/operators'; | ||
| import { Atom, DataService } from '../data.service'; | ||
|
|
||
| const EXAMPLE_TAGS = [ | ||
|
|
@@ -21,34 +23,45 @@ export class ExampleDataProviderComponent { | |
|
|
||
| page$: Observable<Atom[]>; | ||
|
|
||
| dataProvider: DataProvider<Atom>; | ||
| gridModel = this.qgrid.model(); | ||
|
|
||
| constructor( | ||
| private dataService: DataService, | ||
| ) { } | ||
|
|
||
| onRequestRows(gridModel: GridModel): void { | ||
| private qgrid: Grid, | ||
| ) { | ||
| const server = new FakeServer(this.dataService); | ||
| const pager = gridModel.pagination(); | ||
|
|
||
| this.dataProvider = new DataProvider<Atom>(this.gridModel, [ | ||
| new RequestTotalCountOnceStategy(server), | ||
| new CacheAlreadyRequestedPageStrategy(server, 2), | ||
| new ReverseDataStrategy(), | ||
| ]); | ||
| } | ||
|
|
||
| this.page$ = server.getTotal() | ||
| .pipe( | ||
| tap(total => gridModel.pagination({ count: total })), | ||
| switchMap(() => server.getPage(pager.current, pager.size)), | ||
| ); | ||
| onRequestRows(gridModel: GridModel): void { | ||
| this.page$ = this.dataProvider.getPage(); | ||
| } | ||
| } | ||
|
|
||
| class FakeServer { | ||
| class FakeServer implements DataProviderServer<Atom> { | ||
| constructor( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @DataProviderCache()
@PataProviderBackground()
getPage() {}
@DataProviderCache()
@PataProviderBackground()
getPage() {} |
||
| private dataService: DataService, | ||
| ) { } | ||
|
|
||
| getPage(pageNumber: number, pageSize: number): Observable<Atom[]> { | ||
| getPage(number: number, pageSize: number): Observable<Atom[]> { | ||
| return this.dataService.getAtoms() | ||
| .pipe(map(atoms => atoms.splice(pageNumber * pageSize, pageSize))); | ||
| .pipe(map(atoms => atoms.splice(number * pageSize, pageSize))); | ||
| } | ||
|
|
||
| getTotal(): Observable<number> { | ||
| return this.dataService.getAtoms() | ||
| .pipe(map(atoms => atoms.length)); | ||
| } | ||
| } | ||
|
|
||
| class ReverseDataStrategy<T> extends DataProviderStrategy<T> { | ||
| processData(memo: T[]): Observable<T[]> { | ||
| return of(memo.slice().reverse()); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why we need to do memo slice?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rename it please to ExampleReverseDataStrategy
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
.reverse() mutates initial array |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import { GridModel } from '@qgrid/ngx'; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can u put all strategies directly to the data-provider folder? also remove please index.ts file, we don't use them
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
| import { Observable } from 'rxjs'; | ||
|
|
||
| export abstract class DataProviderStrategy<T> { | ||
| protected gridModel: GridModel; | ||
|
|
||
| abstract processData(memo: T[]): Observable<T[]>; | ||
|
||
|
|
||
| setGridModel(model: GridModel): DataProviderStrategy<T> { | ||
| this.gridModel = model; | ||
| return this; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import { GridModel } from '@qgrid/ngx'; | ||
| import { Observable, of } from 'rxjs'; | ||
| import { switchMap } from 'rxjs/operators'; | ||
| import { DataProviderStrategy } from './data-provider-strategy'; | ||
|
|
||
| export class DataProvider<T> { | ||
|
|
||
| constructor( | ||
| private gridModel: GridModel, | ||
| private strategies: DataProviderStrategy<T>[], | ||
| ) { | ||
| strategies.forEach(strategy => strategy.setGridModel(this.gridModel)); | ||
| } | ||
|
|
||
| getPage(): Observable<T[]> { | ||
| return this.applyStrategies(); | ||
| } | ||
|
|
||
| private applyStrategies(memo = [], index = 0): Observable<T[]> { | ||
| const strategy = this.strategies[index]; | ||
| const hasNext = !!this.strategies[index + 1]; | ||
| if (!strategy) { | ||
| return of(memo); | ||
| } | ||
| return strategy.processData(memo) | ||
| .pipe(switchMap(x => hasNext ? this.applyStrategies(x, index + 1) : of(x))); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import { GridModel } from '@qgrid/ngx'; | ||
|
||
| import { Observable } from 'rxjs'; | ||
|
|
||
| export interface DataProviderOptions<T> { | ||
|
||
| server: DataProviderServer<T>; | ||
| gridModel: GridModel, | ||
| pageSize?: number; | ||
| } | ||
|
|
||
| export interface DataProviderServer<T> { | ||
| getPage(page?: number, size?: number): Observable<T[]>; | ||
|
||
| getTotal(): Observable<number>; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import { Observable, of } from 'rxjs'; | ||
| import { filter, tap } from 'rxjs/operators'; | ||
| import { DataProviderStrategy } from '../data-provider-strategy'; | ||
| import { DataProviderServer } from '../models'; | ||
|
|
||
| export class CacheAlreadyRequestedPageStrategy<T> extends DataProviderStrategy<T> { | ||
| private gridRowsCache: Map<number, T[]> = new Map(); | ||
|
||
|
|
||
| constructor( | ||
| private server: Pick<DataProviderServer<T>, 'getPage'>, | ||
| private pagesToLoad: number = 0, | ||
|
||
| ) { | ||
| super(); | ||
| } | ||
|
|
||
| processData(memo: T[]): Observable<T[]> { | ||
| const { current, size } = this.gridModel.pagination(); | ||
|
|
||
| if (this.pagesToLoad) { | ||
| this.loadInBackground(this.pagesToLoad); | ||
| } | ||
|
|
||
| if (this.gridRowsCache.has(current)) { | ||
| return of(this.gridRowsCache.get(current)); | ||
| } | ||
|
|
||
| const shouldRequestData = !memo?.length; | ||
| return (shouldRequestData ? this.server.getPage(current, size) : of(memo)) | ||
| .pipe(tap(rows => this.gridRowsCache.set(current, rows))); | ||
| } | ||
|
|
||
| private loadInBackground(pagesToLoad: number): void { | ||
| const { current, size } = this.gridModel.pagination(); | ||
|
||
| const fromPage = current + 1; | ||
| const toPage = current + pagesToLoad; | ||
| for (let page = fromPage; page <= toPage; page++) { | ||
| if (!this.gridRowsCache.has(page)){ | ||
| this.server.getPage(page, size) | ||
| .pipe(filter(rows => !!rows?.length)) | ||
| .subscribe(rows => this.gridRowsCache.set(page, rows)); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| export { CacheAlreadyRequestedPageStrategy } from './cache-requested-pages'; | ||
| export { RequestTotalCountOnceStategy } from './request-count-once'; | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import { Observable, of } from 'rxjs'; | ||
| import { switchMap, tap } from 'rxjs/operators'; | ||
| import { DataProviderStrategy } from '../data-provider-strategy'; | ||
| import { DataProviderServer } from '../models'; | ||
|
|
||
|
|
||
| export class RequestTotalCountOnceStategy<T> extends DataProviderStrategy<T> { | ||
| private totalCount: number = 0; | ||
|
|
||
klumba12 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| constructor( | ||
| private server: Pick<DataProviderServer<T>, 'getTotal'>, | ||
| ) { | ||
| super(); | ||
| } | ||
|
|
||
| processData(memo: T[]): Observable<T[]> { | ||
| if (this.totalCount > 0) { | ||
| this.setPaginationCount(this.totalCount); | ||
| return of(memo); | ||
| } | ||
| return this.server.getTotal() | ||
|
||
| .pipe( | ||
| tap(count => { | ||
| this.totalCount = count; | ||
| this.setPaginationCount(count); | ||
| }), | ||
| switchMap(() => of(memo)) | ||
| ) | ||
| } | ||
|
|
||
| private setPaginationCount(count: number): void { | ||
| this.gridModel.pagination({ count }); | ||
|
||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
initialize dataPovider right here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make it private
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we again try to initialize dataProvider right here?