Skip to content

Commit

Permalink
feat: 🎸 Add option to support multiple query forms at the same
Browse files Browse the repository at this point in the history
  • Loading branch information
TomTomB committed Feb 15, 2024
1 parent 64b81fe commit 8d78b59
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 29 deletions.
5 changes: 5 additions & 0 deletions .changeset/smooth-jars-divide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ethlete/query": minor
---

Add option to support multiple query forms at the same time
5 changes: 2 additions & 3 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@ node_modules
pnpm-lock.yaml
migrations.json

.changeset/pre.json
.changeset/*.md

libs/types/src/lib/api
/.nx/cache
.angular

.changeset/pre.json
.changeset/*.md
CHANGELOG.md
4 changes: 2 additions & 2 deletions apps/playground/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ <h1>Playground</h1>

<et-query-devtools />

<ethlete-arch-test-accordion>
<!-- <ethlete-arch-test-accordion>
<ethlete-arch-test-accordion-item [isExpanded]="true" label="Accordion 1">
<p>Some accordion stuff</p>
</ethlete-arch-test-accordion-item>
Expand Down Expand Up @@ -73,4 +73,4 @@ <h1>Playground</h1>
<button [ethleteOverlayTrigger]="{ component: comp, inputs: { foo: 'with context' }, injector }">
overlay component custom
</button>
</button> -->
74 changes: 55 additions & 19 deletions apps/playground/src/app/query/form/form.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ export const postRefreshToken = client.post({
</pre
>
<h1>Form 2</h1>
<form [formGroup]="form2.form">
<input [formControl]="form2.controls.query" type="text" placeholder="Query" />
<input [formControl]="form2.controls.page" type="number" placeholder="Page" />
<input [formControl]="form2.controls.limit" type="number" placeholder="Limit" />
</form>
`,
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
Expand All @@ -129,23 +136,47 @@ export class QueryFormComponent {
protected readonly usersQuery = getUsers.createSignal(null, { abortPrevious: true });
private readonly _destroy$ = createDestroy();

form = new QueryForm({
query: new QueryField({
control: new FormControl<string>(''),
debounce: 300,
disableDebounceIfFalsy: true,
}),
page: new QueryField({
control: new FormControl<number>(1),
isResetBy: ['query', 'limit'],
}),
limit: new QueryField({ control: new FormControl<number>(10) }),
sort: new QueryField<Sort | null>({
control: new FormControl(),
queryParamToValueTransformFn: transformToSort,
valueToQueryParamTransformFn: transformToSortQueryParam,
}),
});
form = new QueryForm(
{
query: new QueryField({
control: new FormControl<string>(''),
debounce: 300,
disableDebounceIfFalsy: true,
}),
page: new QueryField({
control: new FormControl<number>(1),
isResetBy: ['query', 'limit'],
}),
limit: new QueryField({ control: new FormControl<number>(10) }),
sort: new QueryField<Sort | null>({
control: new FormControl(),
queryParamToValueTransformFn: transformToSort,
valueToQueryParamTransformFn: transformToSortQueryParam,
}),
},
{ queryParamPrefix: 'form' },
);

form2 = new QueryForm(
{
query: new QueryField({
control: new FormControl<string>(''),
debounce: 300,
disableDebounceIfFalsy: true,
}),
page: new QueryField({
control: new FormControl<number>(1),
isResetBy: ['query', 'limit'],
}),
limit: new QueryField({ control: new FormControl<number>(10) }),
sort: new QueryField<Sort | null>({
control: new FormControl(),
queryParamToValueTransformFn: transformToSort,
valueToQueryParamTransformFn: transformToSortQueryParam,
}),
},
{ queryParamPrefix: 'form-2' },
);

loginForm = new FormGroup({
username: new FormControl<string>(''),
Expand All @@ -155,15 +186,20 @@ export class QueryFormComponent {
constructor() {
this._setAp();

setTimeout(() => {
this.form2.form.controls.query.setValue('test');
}, 100);

this.form.observe();
this.form2.observe();

client.authProvider$
.pipe(
takeUntilDestroyed(),
filter((ap): ap is BearerAuthProvider<typeof postRefreshToken> => !!ap && isBearerAuthProvider(ap)),
switchMap((ap) => ap.tokens$),
filter((tokens) => !!tokens.token && !!tokens.refreshToken),
tap(() => {
this.form.observe();

this.form.changes$
.pipe(
takeUntil(this._destroy$),
Expand Down
26 changes: 21 additions & 5 deletions libs/query/src/lib/query-form/query-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ export class QueryField<T> {

const IGNORED_FILTER_COUNT_FIELDS = ['page', 'skip', 'take', 'limit', 'sort', 'sortBy', 'sortOrder', 'query', 'search'];

export interface QueryFormOptions {
/**
* A prefix to use for the query parameters. This is useful when you have multiple query forms on the same page.
*/
queryParamPrefix?: string;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export class QueryForm<T extends Record<string, QueryField<any>>> {
private readonly _destroy$ = createDestroy();
Expand Down Expand Up @@ -117,7 +124,11 @@ export class QueryForm<T extends Record<string, QueryField<any>>> {
}, {} as QueryFormValue<T>);
}

constructor(private _fields: T) {
// with prefix eg. page should become ${prefix}-page
constructor(
private _fields: T,
private _options?: QueryFormOptions,
) {
assertInInjectionContext(QueryForm);
}

Expand Down Expand Up @@ -300,7 +311,7 @@ export class QueryForm<T extends Record<string, QueryField<any>>> {
let didValueChanges = false;

for (const [key, field] of Object.entries(this._fields)) {
const value = options.queryParams[key];
const value = options.queryParams[this._transformKeyToQueryParam(key)];

const valueDoesNotExist = value === undefined;

Expand Down Expand Up @@ -361,6 +372,10 @@ export class QueryForm<T extends Record<string, QueryField<any>>> {
return val ?? null;
}

private _transformKeyToQueryParam(key: string) {
return this._options?.queryParamPrefix ? `${this._options.queryParamPrefix}-${key}` : key;
}

private _isDefaultValue(key: string, value: unknown) {
const normalizedValue = Array.isArray(value) ? `${ET_ARR_PREFIX}${JSON.stringify(value)}` : value;

Expand Down Expand Up @@ -397,17 +412,17 @@ export class QueryForm<T extends Record<string, QueryField<any>>> {
const queryParams = { ...clone(this._activatedRoute.snapshot.queryParams) };

for (const [key, value] of Object.entries(values)) {
const queryParamKey = this._transformKeyToQueryParam(key);
const field = this._fields[key];

if (!field) {
console.warn(`The field "${key}" is not defined in the QueryForm. Is it a typo?`, this);
continue;
}

if (this._isDefaultValue(key, value) || field.data.appendToUrl === false) {
delete queryParams[key];
queryParams[queryParamKey] = undefined;
} else {
queryParams[key] = field.data.valueToQueryParamTransformFn
queryParams[queryParamKey] = field.data.valueToQueryParamTransformFn
? field.data.valueToQueryParamTransformFn?.(value)
: value;
}
Expand All @@ -417,6 +432,7 @@ export class QueryForm<T extends Record<string, QueryField<any>>> {
this._router.navigate([], {
queryParams,
replaceUrl,
queryParamsHandling: 'merge',
});
});
}
Expand Down

0 comments on commit 8d78b59

Please sign in to comment.