Commit 7a19dee3eb4a441bf17dfd99e9978351bf5862fa
1 parent
cf6824cf
UI: Dynamic filter value configuration
Showing
9 changed files
with
90 additions
and
18 deletions
... | ... | @@ -27,25 +27,33 @@ public class FilterPredicateValue<T> { |
27 | 27 | @Getter |
28 | 28 | private final T defaultValue; |
29 | 29 | @Getter |
30 | + private final T userValue; | |
31 | + @Getter | |
30 | 32 | private final DynamicValue<T> dynamicValue; |
31 | 33 | |
32 | 34 | public FilterPredicateValue(T defaultValue) { |
33 | - this(defaultValue, null); | |
35 | + this(defaultValue, null, null); | |
34 | 36 | } |
35 | 37 | |
36 | 38 | @JsonCreator |
37 | 39 | public FilterPredicateValue(@JsonProperty("defaultValue") T defaultValue, |
40 | + @JsonProperty("userValue") T userValue, | |
38 | 41 | @JsonProperty("dynamicValue") DynamicValue<T> dynamicValue) { |
39 | 42 | this.defaultValue = defaultValue; |
43 | + this.userValue = userValue; | |
40 | 44 | this.dynamicValue = dynamicValue; |
41 | 45 | } |
42 | 46 | |
43 | 47 | @JsonIgnore |
44 | 48 | public T getValue() { |
45 | - if (this.dynamicValue != null && this.dynamicValue.getResolvedValue() != null) { | |
46 | - return this.dynamicValue.getResolvedValue(); | |
49 | + if (this.userValue != null) { | |
50 | + return this.userValue; | |
47 | 51 | } else { |
48 | - return defaultValue; | |
52 | + if (this.dynamicValue != null && this.dynamicValue.getResolvedValue() != null) { | |
53 | + return this.dynamicValue.getResolvedValue(); | |
54 | + } else { | |
55 | + return defaultValue; | |
56 | + } | |
49 | 57 | } |
50 | 58 | } |
51 | 59 | ... | ... |
... | ... | @@ -15,7 +15,7 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<form [formGroup]="complexFilterFormGroup" (ngSubmit)="save()" style="width: 700px;"> | |
18 | +<form [formGroup]="complexFilterFormGroup" (ngSubmit)="save()" style="width: 900px;"> | |
19 | 19 | <mat-toolbar color="primary"> |
20 | 20 | <h2 translate>filter.complex-filter</h2> |
21 | 21 | <span fxFlex></span> | ... | ... |
... | ... | @@ -16,7 +16,7 @@ |
16 | 16 | |
17 | 17 | --> |
18 | 18 | <div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" [formGroup]="filterPredicateValueFormGroup"> |
19 | - <div fxFlex fxLayout="column"> | |
19 | + <div fxFlex fxLayout="column" [fxShow]="!dynamicMode"> | |
20 | 20 | <div fxFlex fxLayout="column" [ngSwitch]="valueType"> |
21 | 21 | <ng-template [ngSwitchCase]="valueTypeEnum.STRING"> |
22 | 22 | <mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block"> |
... | ... | @@ -43,6 +43,40 @@ |
43 | 43 | </mat-checkbox> |
44 | 44 | </ng-template> |
45 | 45 | </div> |
46 | - <div class="tb-hint">Default value</div> | |
46 | + <div class="tb-hint" translate>filter.default-value</div> | |
47 | 47 | </div> |
48 | + <div fxFlex fxLayout="column" [fxShow]="dynamicMode"> | |
49 | + <div fxFlex formGroupName="dynamicValue" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> | |
50 | + <div fxFlex fxLayout="column"> | |
51 | + <mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block"> | |
52 | + <mat-label></mat-label> | |
53 | + <mat-select formControlName="sourceType" placeholder="{{'filter.dynamic-source-type' | translate}}"> | |
54 | + <mat-option [value]="null"> | |
55 | + {{'filter.no-dynamic-value' | translate}} | |
56 | + </mat-option> | |
57 | + <mat-option *ngFor="let sourceType of dynamicValueSourceTypes" [value]="sourceType"> | |
58 | + {{dynamicValueSourceTypeTranslations.get(dynamicValueSourceTypeEnum[sourceType]) | translate}} | |
59 | + </mat-option> | |
60 | + </mat-select> | |
61 | + </mat-form-field> | |
62 | + <div class="tb-hint" translate>filter.dynamic-source-type</div> | |
63 | + </div> | |
64 | + <div fxFlex fxLayout="column"> | |
65 | + <mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block"> | |
66 | + <mat-label></mat-label> | |
67 | + <input matInput formControlName="sourceAttribute" placeholder="{{'filter.source-attribute' | translate}}"> | |
68 | + </mat-form-field> | |
69 | + <div class="tb-hint" translate>filter.source-attribute</div> | |
70 | + </div> | |
71 | + </div> | |
72 | + </div> | |
73 | + <button mat-icon-button | |
74 | + class="mat-elevation-z1 tb-mat-32" | |
75 | + color="primary" | |
76 | + type="button" | |
77 | + matTooltip="{{ (dynamicMode ? 'filter.switch-to-default-value' : 'filter.switch-to-dynamic-value') | translate }}" | |
78 | + matTooltipPosition="above" | |
79 | + (click)="dynamicMode = !dynamicMode"> | |
80 | + <mat-icon class="tb-mat-20" [svgIcon]="dynamicMode ? 'mdi:numeric' : 'mdi:variable'"></mat-icon> | |
81 | + </button> | |
48 | 82 | </div> | ... | ... |
... | ... | @@ -23,7 +23,12 @@ import { |
23 | 23 | ValidatorFn, |
24 | 24 | Validators |
25 | 25 | } from '@angular/forms'; |
26 | -import { EntityKeyValueType, FilterPredicateValue } from '@shared/models/query/query.models'; | |
26 | +import { | |
27 | + DynamicValueSourceType, | |
28 | + dynamicValueSourceTypeTranslationMap, | |
29 | + EntityKeyValueType, | |
30 | + FilterPredicateValue | |
31 | +} from '@shared/models/query/query.models'; | |
27 | 32 | |
28 | 33 | @Component({ |
29 | 34 | selector: 'tb-filter-predicate-value', |
... | ... | @@ -46,8 +51,14 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn |
46 | 51 | |
47 | 52 | valueTypeEnum = EntityKeyValueType; |
48 | 53 | |
54 | + dynamicValueSourceTypes = Object.keys(DynamicValueSourceType); | |
55 | + dynamicValueSourceTypeEnum = DynamicValueSourceType; | |
56 | + dynamicValueSourceTypeTranslations = dynamicValueSourceTypeTranslationMap; | |
57 | + | |
49 | 58 | filterPredicateValueFormGroup: FormGroup; |
50 | 59 | |
60 | + dynamicMode = false; | |
61 | + | |
51 | 62 | private propagateChange = null; |
52 | 63 | |
53 | 64 | constructor(private fb: FormBuilder) { |
... | ... | @@ -83,6 +94,13 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn |
83 | 94 | } |
84 | 95 | ) |
85 | 96 | }); |
97 | + this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceType').valueChanges.subscribe( | |
98 | + (sourceType) => { | |
99 | + if (!sourceType) { | |
100 | + this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceAttribute').patchValue(null, {emitEvent: false}); | |
101 | + } | |
102 | + } | |
103 | + ); | |
86 | 104 | this.filterPredicateValueFormGroup.valueChanges.subscribe(() => { |
87 | 105 | this.updateModel(); |
88 | 106 | }); |
... | ... | @@ -106,8 +124,10 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn |
106 | 124 | |
107 | 125 | writeValue(predicateValue: FilterPredicateValue<string | number | boolean>): void { |
108 | 126 | this.filterPredicateValueFormGroup.get('defaultValue').patchValue(predicateValue.defaultValue, {emitEvent: false}); |
109 | - this.filterPredicateValueFormGroup.get('dynamicValue').patchValue(predicateValue.dynamicValue ? | |
110 | - predicateValue.dynamicValue : { sourceType: null, sourceAttribute: null }, {emitEvent: false}); | |
127 | + this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceType').patchValue(predicateValue.dynamicValue ? | |
128 | + predicateValue.dynamicValue.sourceType : null, {emitEvent: false}); | |
129 | + this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceAttribute').patchValue(predicateValue.dynamicValue ? | |
130 | + predicateValue.dynamicValue.sourceAttribute : null, {emitEvent: false}); | |
111 | 131 | } |
112 | 132 | |
113 | 133 | private updateModel() { | ... | ... |
... | ... | @@ -16,7 +16,7 @@ |
16 | 16 | |
17 | 17 | --> |
18 | 18 | <div fxLayout="column" class="mat-content mat-padding"> |
19 | - <div fxLayout="column" *ngFor="let filter of filtersInfo | keyvalue"> | |
19 | + <div fxLayout="column" *ngFor="let filter of filtersInfo | keyvalue; let last = last"> | |
20 | 20 | <div fxFlex fxLayout="row" fxLayoutAlign="start center"> |
21 | 21 | <mat-label fxFlex>{{filter.value.filter}}</mat-label> |
22 | 22 | <button mat-icon-button color="primary" |
... | ... | @@ -28,6 +28,6 @@ |
28 | 28 | <mat-icon>edit</mat-icon> |
29 | 29 | </button> |
30 | 30 | </div> |
31 | - <mat-divider></mat-divider> | |
31 | + <mat-divider *ngIf="!last"></mat-divider> | |
32 | 32 | </div> |
33 | 33 | </div> | ... | ... |
... | ... | @@ -15,7 +15,7 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<form [formGroup]="keyFilterFormGroup" (ngSubmit)="save()" style="width: 700px;"> | |
18 | +<form [formGroup]="keyFilterFormGroup" (ngSubmit)="save()" style="width: 900px;"> | |
19 | 19 | <mat-toolbar color="primary"> |
20 | 20 | <h2>{{(data.isAdd ? 'filter.add-key-filter' : 'filter.edit-key-filter') | translate}}</h2> |
21 | 21 | <span fxFlex></span> | ... | ... |
... | ... | @@ -33,10 +33,11 @@ import { DialogComponent } from '@app/shared/components/dialog.component'; |
33 | 33 | import { TranslateService } from '@ngx-translate/core'; |
34 | 34 | import { |
35 | 35 | EntityKeyValueType, |
36 | - Filter, | |
36 | + Filter, FilterPredicateValue, | |
37 | 37 | filterToUserFilterInfoList, |
38 | 38 | UserFilterInputInfo |
39 | 39 | } from '@shared/models/query/query.models'; |
40 | +import { isDefinedAndNotNull } from '@core/utils'; | |
40 | 41 | |
41 | 42 | export interface UserFilterDialogData { |
42 | 43 | filter: Filter; |
... | ... | @@ -81,15 +82,17 @@ export class UserFilterDialogComponent extends DialogComponent<UserFilterDialogC |
81 | 82 | } |
82 | 83 | |
83 | 84 | private createUserInputFormControl(userInput: UserFilterInputInfo): AbstractControl { |
85 | + const predicateValue: FilterPredicateValue<string | number | boolean> = (userInput.info.keyFilterPredicate as any).value; | |
86 | + const value = isDefinedAndNotNull(predicateValue.userValue) ? predicateValue.userValue : predicateValue.defaultValue; | |
84 | 87 | const userInputControl = this.fb.group({ |
85 | 88 | label: [userInput.label], |
86 | 89 | valueType: [userInput.valueType], |
87 | - value: [(userInput.info.keyFilterPredicate as any).value.defaultValue, | |
90 | + value: [value, | |
88 | 91 | userInput.valueType === EntityKeyValueType.NUMERIC || |
89 | 92 | userInput.valueType === EntityKeyValueType.DATE_TIME ? [Validators.required] : []] |
90 | 93 | }); |
91 | - userInputControl.get('value').valueChanges.subscribe(value => { | |
92 | - (userInput.info.keyFilterPredicate as any).value.defaultValue = value; | |
94 | + userInputControl.get('value').valueChanges.subscribe(userValue => { | |
95 | + (userInput.info.keyFilterPredicate as any).value.userValue = userValue; | |
93 | 96 | }); |
94 | 97 | return userInputControl; |
95 | 98 | } | ... | ... |
... | ... | @@ -1230,7 +1230,13 @@ |
1230 | 1230 | "time": "Time", |
1231 | 1231 | "current-tenant": "Current tenant", |
1232 | 1232 | "current-customer": "Current customer", |
1233 | - "current-user": "Current user" | |
1233 | + "current-user": "Current user", | |
1234 | + "default-value": "Default value", | |
1235 | + "dynamic-source-type": "Dynamic source type", | |
1236 | + "no-dynamic-value": "No dynamic value", | |
1237 | + "source-attribute": "Source attribute", | |
1238 | + "switch-to-dynamic-value": "Switch to dynamic value", | |
1239 | + "switch-to-default-value": "Switch to default value" | |
1234 | 1240 | }, |
1235 | 1241 | "fullscreen": { |
1236 | 1242 | "expand": "Expand to fullscreen", | ... | ... |