mirror of https://github.com/abpframework/abp.git
3 changed files with 247 additions and 0 deletions
@ -0,0 +1,96 @@ |
|||||
|
import { Inject, Injectable, Optional } from '@angular/core'; |
||||
|
import { BehaviorSubject, Observable, ReplaySubject } from 'rxjs'; |
||||
|
import { debounceTime, shareReplay, switchMap, tap } from 'rxjs/operators'; |
||||
|
import { ABP } from '../models/common'; |
||||
|
import { LIST_QUERY_DEBOUNCE_TIME } from '../tokens/list.token'; |
||||
|
import { takeUntilDestroy } from '../utils/rxjs-utils'; |
||||
|
|
||||
|
@Injectable() |
||||
|
export class ListService { |
||||
|
private _filter = ''; |
||||
|
set filter(value: string) { |
||||
|
this._filter = value; |
||||
|
this.get(); |
||||
|
} |
||||
|
get filter(): string { |
||||
|
return this._filter; |
||||
|
} |
||||
|
|
||||
|
private _maxResultCount = 10; |
||||
|
set maxResultCount(value: number) { |
||||
|
this._maxResultCount = value; |
||||
|
this.get(); |
||||
|
} |
||||
|
get maxResultCount(): number { |
||||
|
return this._maxResultCount; |
||||
|
} |
||||
|
|
||||
|
private _page = 1; |
||||
|
set page(value: number) { |
||||
|
this._page = value; |
||||
|
this.get(); |
||||
|
} |
||||
|
get page(): number { |
||||
|
return this._page; |
||||
|
} |
||||
|
|
||||
|
private _sortKey = ''; |
||||
|
set sortKey(value: string) { |
||||
|
console.log(value); |
||||
|
this._sortKey = value; |
||||
|
this.get(); |
||||
|
} |
||||
|
get sortKey(): string { |
||||
|
return this._sortKey; |
||||
|
} |
||||
|
|
||||
|
private _sortOrder = ''; |
||||
|
set sortOrder(value: string) { |
||||
|
this._sortOrder = value; |
||||
|
this.get(); |
||||
|
} |
||||
|
get sortOrder(): string { |
||||
|
return this._sortOrder; |
||||
|
} |
||||
|
|
||||
|
private _query$ = new ReplaySubject<ABP.PageQueryParams>(1); |
||||
|
|
||||
|
get query$(): Observable<ABP.PageQueryParams> { |
||||
|
return this._query$ |
||||
|
.asObservable() |
||||
|
.pipe(debounceTime(this.delay || 300), shareReplay({ bufferSize: 1, refCount: true })); |
||||
|
} |
||||
|
|
||||
|
private _isLoading$ = new BehaviorSubject(false); |
||||
|
|
||||
|
get isLoading$(): Observable<boolean> { |
||||
|
return this._isLoading$.asObservable(); |
||||
|
} |
||||
|
|
||||
|
constructor(@Optional() @Inject(LIST_QUERY_DEBOUNCE_TIME) private delay: number) { |
||||
|
this.get(); |
||||
|
} |
||||
|
|
||||
|
get() { |
||||
|
this._query$.next({ |
||||
|
filter: this._filter || undefined, |
||||
|
maxResultCount: this._maxResultCount, |
||||
|
skipCount: (this._page - 1) * this._maxResultCount, |
||||
|
sorting: this._sortOrder ? `${this._sortKey} ${this._sortOrder}` : undefined, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
hookToQuery<T extends any>(streamCreatorCallback: QueryStreamCreatorCallback<T>): Observable<T> { |
||||
|
this._isLoading$.next(true); |
||||
|
|
||||
|
return this.query$.pipe( |
||||
|
switchMap(streamCreatorCallback), |
||||
|
tap(() => this._isLoading$.next(false)), |
||||
|
takeUntilDestroy(this), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
ngOnDestroy() {} |
||||
|
} |
||||
|
|
||||
|
export type QueryStreamCreatorCallback<T> = (query: ABP.PageQueryParams) => Observable<T>; |
||||
@ -0,0 +1,150 @@ |
|||||
|
import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; |
||||
|
import { of } from 'rxjs'; |
||||
|
import { bufferCount, take } from 'rxjs/operators'; |
||||
|
import { ListService } from '../services/list.service'; |
||||
|
import { LIST_QUERY_DEBOUNCE_TIME } from '../tokens'; |
||||
|
|
||||
|
describe('ListService', () => { |
||||
|
let spectator: SpectatorService<ListService>; |
||||
|
let service: ListService; |
||||
|
|
||||
|
const createService = createServiceFactory({ |
||||
|
service: ListService, |
||||
|
providers: [ |
||||
|
{ |
||||
|
provide: LIST_QUERY_DEBOUNCE_TIME, |
||||
|
useValue: 0, |
||||
|
}, |
||||
|
], |
||||
|
}); |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
spectator = createService(); |
||||
|
service = spectator.service; |
||||
|
}); |
||||
|
|
||||
|
describe('#filter', () => { |
||||
|
it('should initially be empty string', () => { |
||||
|
expect(service.filter).toBe(''); |
||||
|
}); |
||||
|
|
||||
|
it('should be changed', () => { |
||||
|
service.filter = 'foo'; |
||||
|
|
||||
|
expect(service.filter).toBe('foo'); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
describe('#maxResultCount', () => { |
||||
|
it('should initially be 10', () => { |
||||
|
expect(service.maxResultCount).toBe(10); |
||||
|
}); |
||||
|
|
||||
|
it('should be changed', () => { |
||||
|
service.maxResultCount = 20; |
||||
|
|
||||
|
expect(service.maxResultCount).toBe(20); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
describe('#page', () => { |
||||
|
it('should initially be 1', () => { |
||||
|
expect(service.page).toBe(1); |
||||
|
}); |
||||
|
|
||||
|
it('should be changed', () => { |
||||
|
service.page = 9; |
||||
|
|
||||
|
expect(service.page).toBe(9); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
describe('#sortKey', () => { |
||||
|
it('should initially be empty string', () => { |
||||
|
expect(service.sortKey).toBe(''); |
||||
|
}); |
||||
|
|
||||
|
it('should be changed', () => { |
||||
|
service.sortKey = 'foo'; |
||||
|
|
||||
|
expect(service.sortKey).toBe('foo'); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
describe('#sortOrder', () => { |
||||
|
it('should initially be empty string', () => { |
||||
|
expect(service.sortOrder).toBe(''); |
||||
|
}); |
||||
|
|
||||
|
it('should be changed', () => { |
||||
|
service.sortOrder = 'foo'; |
||||
|
|
||||
|
expect(service.sortOrder).toBe('foo'); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
describe('#query$', () => { |
||||
|
it('should initially emit default query', done => { |
||||
|
service.query$.pipe(take(1)).subscribe(query => { |
||||
|
expect(query).toEqual({ |
||||
|
filter: undefined, |
||||
|
maxResultCount: 10, |
||||
|
skipCount: 0, |
||||
|
sorting: undefined, |
||||
|
}); |
||||
|
|
||||
|
done(); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
it('should emit a query based on params set', done => { |
||||
|
service.filter = 'foo'; |
||||
|
service.sortKey = 'bar'; |
||||
|
service.sortOrder = 'baz'; |
||||
|
service.maxResultCount = 20; |
||||
|
service.page = 9; |
||||
|
|
||||
|
service.query$.pipe(take(1)).subscribe(query => { |
||||
|
expect(query).toEqual({ |
||||
|
filter: 'foo', |
||||
|
sorting: 'bar baz', |
||||
|
maxResultCount: 20, |
||||
|
skipCount: 160, |
||||
|
}); |
||||
|
|
||||
|
done(); |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
describe('#hookToQuery', () => { |
||||
|
it('should call given callback with the query', done => { |
||||
|
const callback = query => of(query); |
||||
|
|
||||
|
service.hookToQuery(callback).subscribe(query => { |
||||
|
expect(query).toEqual({ |
||||
|
filter: undefined, |
||||
|
maxResultCount: 10, |
||||
|
skipCount: 0, |
||||
|
sorting: undefined, |
||||
|
}); |
||||
|
|
||||
|
done(); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
it('should emit isLoading as side effect', done => { |
||||
|
const callback = query => of(query); |
||||
|
|
||||
|
service.isLoading$.pipe(bufferCount(3)).subscribe(([idle, init, end]) => { |
||||
|
expect(idle).toBe(false); |
||||
|
expect(init).toBe(true); |
||||
|
expect(end).toBe(false); |
||||
|
|
||||
|
done(); |
||||
|
}); |
||||
|
|
||||
|
service.hookToQuery(callback).subscribe(); |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
Loading…
Reference in new issue