Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions packages/ng2-qgrid/src/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ export {
BlurDirective,
BoolEditorComponent,
BoolEditorModule,
CacheAlreadyRequestedPageStrategy,
CaptionComponent,
CaptionModule,
CellEditorComponent,
Expand All @@ -189,6 +190,12 @@ export {
CurrencyPipe,
DataManipulationComponent,
DataManipulationModule,
DataProvider,
DataProviderComponent,
DataProviderModule,
DataProviderPageServer,
DataProviderProcessContext,
DataProviderStrategy,
DateDirective,
DateMaskDirective,
DateModule,
Expand Down Expand Up @@ -247,10 +254,9 @@ export {
ReferenceComponent,
ReferenceEditorComponent,
ReferenceEditorModule,
RequestTotalCountOnceStategy,
RestComponent,
RestModule,
DataProviderComponent,
DataProviderModule,
RuleComponent,
SerializationService,
StatusBarComponent,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<q-grid [caption]="title">
<q-grid [caption]="title" [model]="gridModel">
<q-grid-columns generation="deep">
</q-grid-columns>

Expand Down
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, DataProviderPageServer, 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 = [
Expand All @@ -12,6 +14,7 @@ const EXAMPLE_TAGS = [
@Component({
selector: 'example-data-provider',
templateUrl: 'example-data-provider.component.html',
styleUrls: ['example-data-provider.component.scss'],
providers: [DataService],
changeDetection: ChangeDetectionStrategy.OnPush
})
Expand All @@ -21,34 +24,46 @@ export class ExampleDataProviderComponent {

page$: Observable<Atom[]>;

gridModel = this.qgrid.model();

private dataProvider: DataProvider<Atom>;

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, 1),
new ExampleReverseDataStrategy(),
]);
}

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 DataProviderPageServer<Atom> {
constructor(
Copy link
Collaborator

Choose a reason for hiding this comment

The 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 ExampleReverseDataStrategy<T> implements DataProviderStrategy<T> {
process(memo: T[]): Observable<T[]> {
return of(memo.slice().reverse());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why we need to do memo slice?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename it please to ExampleReverseDataStrategy

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why we need to do memo slice?

.reverse() mutates initial array

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { GridModel } from '@qgrid/ngx';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

file name should be cache-already-requested-page-strategy.ts

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

import { Observable, of } from 'rxjs';
import { filter, tap } from 'rxjs/operators';
import { DataProviderPageServer } from './data-provider-page-server';
import { DataProviderProcessContext } from './data-provider-process-context';
import { DataProviderStrategy } from './data-provider-strategy';

export class CacheAlreadyRequestedPageStrategy<T> implements DataProviderStrategy<T> {
private gridRowsCache: Map<number, T[]> = new Map();

constructor(
private server: Pick<DataProviderPageServer<T>, 'getPage'>,
private pagesToLoad: number = 0,
Copy link
Collaborator

@klumba12 klumba12 Mar 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should be options { pagesToLoad: number }, consider to rename pagesToLoad to numberOfBackgroundPages or backgroundPages

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

) { }

process(memo: T[], { model }: DataProviderProcessContext): Observable<T[]> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename memo to data

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

const { current, size } = model.pagination();

if (this.pagesToLoad) {
this.loadInBackground(this.pagesToLoad, model);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if process will conflict with loadInBackground? need to have synchronization mechanizm

}

if (this.gridRowsCache.has(current)) {
return of(this.gridRowsCache.get(current));
}

const shouldRequestData = !memo?.length;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why memo can be undefined? our method signature doesn't accept it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah. It's always at least an empty array. Removed question mark

return (shouldRequestData ? this.server.getPage(current, size) : of(memo))
.pipe(tap(rows => this.gridRowsCache.set(current, rows)));
}

private loadInBackground(pagesToLoad: number, model: GridModel): void {
const { count, current, size } = model.pagination();
const fromPage = current + 1;
const toPage = current + pagesToLoad;
const maxPage = Math.floor(count / size);
for (let page = fromPage; page <= toPage; page++) {
if (page <= maxPage && !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,6 @@
import { Observable } from 'rxjs';

export interface DataProviderPageServer<T> {
getPage(page?: number, size?: number): Observable<T[]>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why page and size are optional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

made them required

getTotal(): Observable<number>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { GridModel } from '@qgrid/ngx';

export interface DataProviderProcessContext {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you rename it to DataProviderContext and put under the data-provider.ts file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

model: GridModel;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Observable } from 'rxjs';
import { DataProviderProcessContext } from './data-provider-process-context';

export interface DataProviderStrategy<T> {
process(memo: T[], context: DataProviderProcessContext): Observable<T[]>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

memo -> data

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
27 changes: 27 additions & 0 deletions packages/qgrid-ngx-plugins/src/lib/data-provider/data-provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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>[],
) { }

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.process(memo, { model: this.gridModel })
.pipe(switchMap(x => hasNext ? this.applyStrategies(x, index + 1) : of(x)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Observable, of } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
import { DataProviderPageServer } from './data-provider-page-server';
import { DataProviderProcessContext } from './data-provider-process-context';
import { DataProviderStrategy } from './data-provider-strategy';

export class RequestTotalCountOnceStategy<T> implements DataProviderStrategy<T> {
private totalCount: number = 0;

constructor(
private server: Pick<DataProviderPageServer<T>, 'getTotal'>,
) { }

process(memo: T[], { model }: DataProviderProcessContext): Observable<T[]> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

memo -> data

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

if (this.totalCount > 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what will happen if count will be 0 from the server?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if there was empty array from server wi will try to send that request everytime. Do you think we should remove it?

model.pagination({ count: this.totalCount });
return of(memo);
}

return this.server.getTotal()
.pipe(
tap(count => {
this.totalCount = count;
model.pagination({ count });
}),
switchMap(() => of(memo))
)
}
}
Loading