Last active
April 19, 2024 15:05
-
-
Save JaimeStill/4d9946849802fd875e1b127ca46ac30f to your computer and use it in GitHub Desktop.
Signal-based Query Processor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<h1 class="mat-title m8">Home</h1> | |
<button mat-button class="m8" (click)="search()">Search</button> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Component, | |
OnInit | |
} from '@angular/core'; | |
import { MatButtonModule } from '@angular/material/button'; | |
import { QuerySource } from '../core/query'; | |
import { HttpClient } from '@angular/common/http'; | |
import { SnackerService } from '../core'; | |
import { environment } from '../../environments/environment'; | |
@Component({ | |
selector: 'home-route', | |
standalone: true, | |
templateUrl: 'home.route.html', | |
styleUrl: 'home.route.scss', | |
imports: [ | |
MatButtonModule | |
], | |
providers: [] | |
}) | |
export class HomeRoute implements OnInit { | |
it: number = 0 | |
source: QuerySource<string>; | |
constructor( | |
http: HttpClient, | |
snacker: SnackerService | |
) { | |
this.source = new QuerySource<any>(http, snacker, environment.api, 'agency/query'); | |
} | |
ngOnInit() { | |
this.source.result$.subscribe(result => console.log(++this.it, result)); | |
} | |
search() { | |
this.source.onSearch('thing'); | |
console.log(this.source.url$()); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { SortDirection } from '@angular/material/sort'; | |
export interface QueryOptions { | |
page: number; | |
pageSize: number; | |
search: string | null; | |
sortProperty: string | null; | |
sortOrder: SortDirection; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export interface QueryResult<T> { | |
page: number; | |
pageSize: number; | |
totalCount: number; | |
data: T[]; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
effect, | |
signal, | |
WritableSignal | |
} from '@angular/core'; | |
import { | |
catchError, | |
Observable, | |
ReplaySubject, | |
throwError | |
} from 'rxjs'; | |
import { HttpClient } from '@angular/common/http'; | |
import { PageEvent } from '@angular/material/paginator'; | |
import { Sort } from '@angular/material/sort'; | |
import { SnackerService } from '../services'; | |
import { QueryUrl } from './query-url'; | |
import { QueryOptions } from './query-options'; | |
import { QueryResult } from './query-result'; | |
export class QuerySource<T> { | |
private repage: boolean = false; | |
private defaults: QueryOptions = { | |
page: 1, | |
pageSize: 20, | |
search: null, | |
sortProperty: null, | |
sortOrder: 'asc' | |
} | |
private url: WritableSignal<QueryUrl>; | |
private result: ReplaySubject<QueryResult<T>> = new ReplaySubject<QueryResult<T>>(1); | |
result$: Observable<QueryResult<T>> = this.result.asObservable(); | |
url$ = (): QueryUrl => this.url(); | |
constructor( | |
private http: HttpClient, | |
private snacker: SnackerService, | |
base: string, | |
endpoint: string, | |
options: Partial<QueryOptions> = {} | |
) { | |
this.url = signal(new QueryUrl( | |
base, | |
endpoint, | |
<QueryOptions>{ | |
...this.defaults, | |
...options | |
} | |
)); | |
effect(() => { | |
const url = this.url().build(this.repage); | |
this.repage = false; | |
this.http.get<QueryResult<T>>(url.toString()) | |
.pipe( | |
catchError(er => throwError(() => new Error(er))) | |
) | |
.subscribe({ | |
next: result => this.result.next(result), | |
error: err => this.snacker.sendErrorMessage(err) | |
}); | |
}); | |
} | |
onPage(event: PageEvent) { | |
this.url.update((value: QueryUrl) => { | |
this.repage = event.pageSize !== value.options.pageSize; | |
value.options.page = event.pageIndex; | |
value.options.pageSize = event.pageSize; | |
return new QueryUrl( | |
value.base, | |
value.endpoint, | |
value.options | |
); | |
}); | |
} | |
onSearch(event: string | null) { | |
this.url.update((value: QueryUrl) => { | |
this.repage = event !== value.options.search; | |
value.options.search = event; | |
return new QueryUrl( | |
value.base, | |
value.endpoint, | |
value.options | |
); | |
}); | |
} | |
onSort(event: Sort | null) { | |
this.url.update((value: QueryUrl) => { | |
if (event) { | |
this.repage = value.options.sortProperty !== event.active; | |
value.options.sortProperty = event.active; | |
value.options.sortOrder = event.direction | |
} else { | |
this.repage = value.options.sortProperty !== null; | |
value.options.sortProperty = null; | |
value.options.sortOrder = 'asc'; | |
} | |
return new QueryUrl( | |
value.base, | |
value.endpoint, | |
value.options | |
); | |
}); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { QueryOptions } from './query-options'; | |
export class QueryUrl { | |
constructor( | |
public base: string, | |
public endpoint: string, | |
public options: QueryOptions | |
) { } | |
build(repage: boolean = false): URL { | |
const url = new URL(this.endpoint, this.base); | |
url.searchParams.set('page', repage ? '1' : this.options.page.toString()); | |
url.searchParams.set('pageSize', `${this.options.sortProperty}_${this.options.sortOrder}`); | |
if (this.options.sortProperty) | |
url.searchParams.set('sort', `${this.options.sortProperty}_${this.options.sortOrder}`); | |
if (this.options.search) | |
url.searchParams.set('search', this.options.search!); | |
return url; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment