Skip to content
Open
Show file tree
Hide file tree
Changes from 10 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,
DataProviderContext,
DataProviderModule,
DataProviderPageServer,
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,43 @@ export class ExampleDataProviderComponent {

page$: Observable<Atom[]>;

gridModel = this.qgrid.model();

private server = new FakeServer(this.dataService);
private dataProvider: DataProvider<Atom> = new DataProvider<Atom>(this.gridModel, [
new RequestTotalCountOnceStategy(this.server),
new CacheAlreadyRequestedPageStrategy(this.server, { pagesToLoad: 1 }),
new ExampleReverseDataStrategy(),
]);

constructor(
private dataService: DataService,
private qgrid: Grid,
) { }

onRequestRows(gridModel: GridModel): void {
const server = new FakeServer(this.dataService);
const pager = gridModel.pagination();

this.page$ = server.getTotal()
.pipe(
tap(total => gridModel.pagination({ count: total })),
switchMap(() => server.getPage(pager.current, pager.size)),
);
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,51 @@
import { GridModel } from '@qgrid/ngx';
import { Observable, of } from 'rxjs';
import { filter, tap } from 'rxjs/operators';
import { DataProviderContext } from './data-provider';
import { DataProviderPageServer } from './data-provider-page-server';
import { DataProviderStrategy } from './data-provider-strategy';

export class CacheAlreadyRequestedPageStrategy<T> implements DataProviderStrategy<T> {
private pageCache: Map<number, T[]>;
private paginationSize: number;

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

Choose a reason for hiding this comment

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

by default pagesToLoad should be equal to 1, if more than 1 do background load

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(data: T[], { model }: DataProviderContext): Observable<T[]> {
const { current, size } = model.pagination();

if (this.paginationSize !== size) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

move it under the separate method like invalidateCache() will be more readable

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

this.pageCache = new Map();
this.paginationSize = size;
}

if (this.options.pagesToLoad) {
this.loadInBackground(this.options.pagesToLoad, model);
}

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

const shouldRequestData = !data.length;
return (shouldRequestData ? this.server.getPage(current, size) : of(data))
Copy link
Collaborator

Choose a reason for hiding this comment

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

with if statement will be more readable

.pipe(tap(rows => this.pageCache.set(current, rows)));
}

private loadInBackground(pagesToLoad: number, model: GridModel): void {
Copy link
Collaborator

Choose a reason for hiding this comment

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

loadBackgroundPages maybe beters?

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 { 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.pageCache.has(page)){
this.server.getPage(page, size)
.pipe(filter(rows => !!rows?.length))
.subscribe(rows => this.pageCache.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[]>;
getTotal(): Observable<number>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Observable } from 'rxjs';
import { DataProviderContext } from './data-provider';

export interface DataProviderStrategy<T> {
process(data: T[], context: DataProviderContext): Observable<T[]>;
}
31 changes: 31 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,31 @@
import { GridModel } from '@qgrid/ngx';
import { Observable, of } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { DataProviderStrategy } from './data-provider-strategy';

export interface DataProviderContext {
model: GridModel;
}

export class DataProvider<T> {

constructor(
private gridModel: GridModel,
private strategies: DataProviderStrategy<T>[],
) { }

getPage(): Observable<T[]> {
return this.applyStrategies();
}

private applyStrategies(data = [], index = 0): Observable<T[]> {
const strategy = this.strategies[index];
const hasNext = !!this.strategies[index + 1];
if (!strategy) {
return of(data);
}

return strategy.process(data, { 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 { DataProviderContext } from './data-provider';
import { DataProviderPageServer } from './data-provider-page-server';
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(data: T[], { model }: DataProviderContext): Observable<T[]> {
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(data);
}

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