Skip to content

Commit 05b48ca

Browse files
alexey-anufrievAlexeyAnufriev
authored andcommitted
feat(core): Added QueryParamDirective for standalone parameters
This allows binding a QueryParam directly to a component without a QueryParamGroup. It works by creating an ad-hoc QueryParamGroup invisible to the user. Signed-off-by: AlexeyAnufriev <ice.sumy@gmail.com>
1 parent 0f6bb62 commit 05b48ca

14 files changed

Lines changed: 276 additions & 67 deletions

projects/ngqp/core/src/lib/accessors/checkbox-control-value-accessor.directive.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const NGQP_CHECKBOX_VALUE_ACCESSOR: any = {
1010

1111
/** @ignore */
1212
@Directive({
13-
selector: 'input[type=checkbox][queryParamName]',
13+
selector: 'input[type=checkbox][queryParamName],input[type=checkbox][queryParam]',
1414
providers: [NGQP_CHECKBOX_VALUE_ACCESSOR],
1515
})
1616
export class CheckboxControlValueAccessorDirective implements ControlValueAccessor {

projects/ngqp/core/src/lib/accessors/default-control-value-accessor.directive.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ function isAndroid(navigator: Navigator): boolean {
1616

1717
/** @ignore */
1818
@Directive({
19-
selector: 'input:not([type=checkbox]):not([type=radio])[queryParamName],textarea[queryParamName]',
19+
selector: 'input:not([type=checkbox]):not([type=radio])[queryParamName],textarea[queryParamName],' +
20+
'input:not([type=checkbox]):not([type=radio])[queryParam],textarea[queryParam]',
2021
providers: [NGQP_DEFAULT_VALUE_ACCESSOR],
2122
})
2223
export class DefaultControlValueAccessorDirective implements ControlValueAccessor {

projects/ngqp/core/src/lib/accessors/multi-select-control-value-accessor.directive.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const NGQP_MULTI_SELECT_VALUE_ACCESSOR: StaticProvider = {
1111

1212
/** @ignore */
1313
@Directive({
14-
selector: 'select[multiple][queryParamName]',
14+
selector: 'select[multiple][queryParamName],select[multiple][queryParam]',
1515
providers: [NGQP_MULTI_SELECT_VALUE_ACCESSOR],
1616
})
1717
export class MultiSelectControlValueAccessorDirective<T> implements ControlValueAccessor {

projects/ngqp/core/src/lib/accessors/number-control-value-accessor.directive.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const NGQP_NUMBER_VALUE_ACCESSOR: any = {
1010

1111
/** @ignore */
1212
@Directive({
13-
selector: 'input[type=number][queryParamName]',
13+
selector: 'input[type=number][queryParamName],input[type=number][queryParam]',
1414
providers: [NGQP_NUMBER_VALUE_ACCESSOR],
1515
})
1616
export class NumberControlValueAccessorDirective implements ControlValueAccessor {

projects/ngqp/core/src/lib/accessors/range-control-value-accessor.directive.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const NGQP_RANGE_VALUE_ACCESSOR: any = {
1010

1111
/** @ignore */
1212
@Directive({
13-
selector: 'input[type=range][queryParamName]',
13+
selector: 'input[type=range][queryParamName],input[type=range][queryParam]',
1414
providers: [NGQP_RANGE_VALUE_ACCESSOR],
1515
})
1616
export class RangeControlValueAccessorDirective implements ControlValueAccessor {

projects/ngqp/core/src/lib/accessors/select-control-value-accessor.directive.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const NGQP_SELECT_VALUE_ACCESSOR: StaticProvider = {
1010

1111
/** @ignore */
1212
@Directive({
13-
selector: 'select:not([multiple])[queryParamName]',
13+
selector: 'select:not([multiple])[queryParamName],select:not([multiple])[queryParam]',
1414
providers: [NGQP_SELECT_VALUE_ACCESSOR],
1515
})
1616
export class SelectControlValueAccessorDirective<T> implements ControlValueAccessor {
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { ControlValueAccessor } from '@angular/forms';
2+
import { DefaultControlValueAccessorDirective } from './default-control-value-accessor.directive';
3+
import { NGQP_BUILT_IN_ACCESSORS } from './ngqp-accessors';
4+
5+
/**
6+
* This resembles the selectControlValueAccessor function from
7+
* https://github.com/angular/angular/blob/7.1.2/packages/forms/src/directives/shared.ts#L186
8+
* We can't use it directly since it isn't exported in the public API, but let's hope choosing
9+
* any accessor is good enough for our purposes.
10+
*
11+
* @internal
12+
*/
13+
export function selectValueAccessor(valueAccessors: ControlValueAccessor[]): ControlValueAccessor | null {
14+
if (!valueAccessors || !Array.isArray(valueAccessors)) {
15+
return null;
16+
}
17+
18+
let defaultAccessor: ControlValueAccessor | null = null;
19+
let builtInAccessor: ControlValueAccessor | null = null;
20+
let customAccessor: ControlValueAccessor | null = null;
21+
22+
valueAccessors.forEach(valueAccessor => {
23+
if (valueAccessor.constructor === DefaultControlValueAccessorDirective) {
24+
defaultAccessor = valueAccessor;
25+
} else if (NGQP_BUILT_IN_ACCESSORS.some(current => valueAccessor.constructor === current)) {
26+
if (builtInAccessor !== null) {
27+
throw new Error(`More than one built-in ControlValueAccessor matches`);
28+
}
29+
30+
builtInAccessor = valueAccessor;
31+
} else {
32+
if (customAccessor !== null) {
33+
throw new Error(`More than one custom ControlValueAccessor has been found on the form control`);
34+
}
35+
36+
customAccessor = valueAccessor;
37+
}
38+
});
39+
40+
if (customAccessor !== null) {
41+
return customAccessor;
42+
}
43+
44+
if (builtInAccessor !== null) {
45+
return builtInAccessor;
46+
}
47+
48+
if (defaultAccessor !== null) {
49+
return defaultAccessor;
50+
}
51+
52+
throw new Error(`No matching ControlValueAccessor has been found for this form control`);
53+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export { QueryParamDirective } from './query-param.directive';
12
export { QueryParamNameDirective } from './query-param-name.directive';
23
export { QueryParamGroupDirective } from './query-param-group.directive';
34
export { ControlValueAccessorDirective } from './control-value-accessor.directive';
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { ControlValueAccessor } from '@angular/forms';
2+
3+
/**
4+
* Unified abstraction for {@link QueryParamNameDirective} and {@link QueryParamDirective}.
5+
* Allows to process query params bound to the group as well as standalone query params.
6+
*
7+
* @internal
8+
*/
9+
export interface QueryParamController {
10+
name: string;
11+
valueAccessor: ControlValueAccessor;
12+
}

projects/ngqp/core/src/lib/directives/query-param-group.service.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { Unpack } from '../types';
1818
import { QueryParamGroup } from '../model/query-param-group';
1919
import { QueryParam } from '../model/query-param';
2020
import { NGQP_ROUTER_ADAPTER, NGQP_ROUTER_OPTIONS, RouterAdapter, RouterOptions } from '../router-adapter/router-adapter.interface';
21-
import { QueryParamNameDirective } from './query-param-name.directive';
21+
import { QueryParamController } from './query-param-controller.interface';
2222

2323
/** @internal */
2424
function isMultiQueryParam<T>(queryParam: QueryParam<T> | QueryParam<T[]>): queryParam is QueryParam<T[]> {
@@ -55,8 +55,8 @@ export class QueryParamGroupService implements OnDestroy {
5555
/** The {@link QueryParamGroup} to bind. */
5656
private queryParamGroup: QueryParamGroup;
5757

58-
/** List of {@link QueryParamNameDirective} directives registered to this service. */
59-
private directives = new Map<string, QueryParamNameDirective[]>();
58+
/** List of {@link QueryParamController} registered to this service. */
59+
private directives = new Map<string, QueryParamController[]>();
6060

6161
/**
6262
* Queue of navigation parameters
@@ -106,9 +106,9 @@ export class QueryParamGroupService implements OnDestroy {
106106
}
107107

108108
/**
109-
* Registers a {@link QueryParamNameDirective} directive.
109+
* Registers a {@link QueryParamController}.
110110
*/
111-
public registerQueryParamDirective(directive: QueryParamNameDirective): void {
111+
public registerQueryParamDirective(directive: QueryParamController): void {
112112
// Capture the name here, particularly for the queue below to avoid re-evaluating
113113
// it as it might change over time.
114114
const queryParamName = directive.name;
@@ -142,7 +142,7 @@ export class QueryParamGroupService implements OnDestroy {
142142
}
143143

144144
/**
145-
* Deregisters a {@link QueryParamNameDirective} directive by referencing its name.
145+
* Deregisters a {@link QueryParamController} by referencing its name.
146146
*/
147147
public deregisterQueryParamDirective(queryParamName: string): void {
148148
if (!queryParamName) {

0 commit comments

Comments
 (0)