Skip to content

Commit 1ef4082

Browse files
guguclaude
andcommitted
Migrate filter field components to Angular signals and fix filter-line layout
Migrate BaseFilterFieldComponent and all 22 concrete filter components from legacy @input()/@output() to Angular signals (input(), output(), computed()). Update all template bindings to use signal calls. Fix custom filter layout so column name and dynamic component render on the same line in both filter dialogs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 89b3d11 commit 1ef4082

43 files changed

Lines changed: 211 additions & 195 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

frontend/src/app/components/dashboard/db-table-view/db-table-filters-dialog/db-table-filters-dialog.component.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,21 @@
1919

2020
.filter-line {
2121
grid-column: 1 / span 4;
22+
display: flex;
23+
align-items: flex-start;
24+
gap: 8px;
25+
}
26+
27+
.filter-line > div,
28+
.filter-line > ng-template,
29+
.filter-line > ndc-dynamic {
30+
flex: 1;
31+
min-width: 0;
32+
}
33+
34+
.filter-line > .column-name {
35+
flex: 0 0 auto;
36+
white-space: nowrap;
2237
}
2338

2439
::ng-deep .mat-dialog-container > .ng-star-inserted {

frontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-dialog/saved-filters-dialog.component.css

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,22 @@
1919

2020
.filter-line {
2121
grid-column: 2 / span 4;
22+
display: flex;
23+
align-items: flex-start;
24+
gap: 8px;
25+
}
26+
27+
.filter-line > div,
28+
.filter-line > ng-template,
29+
.filter-line > ndc-dynamic {
30+
flex: 1;
31+
min-width: 0;
32+
}
33+
34+
.filter-line > .column-name {
35+
flex: 0 0 auto;
36+
white-space: nowrap;
37+
margin-top: 18px;
2238
}
2339

2440
.empty-conditions-container {

frontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-panel.component.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { ActivatedRoute, Router } from '@angular/router';
1515
import JsonURL from '@jsonurl/jsonurl';
1616
import { Angulartics2OnModule } from 'angulartics2';
1717
import { DynamicModule } from 'ng-dynamic-component';
18+
import { SignalComponentIoModule } from 'ng-dynamic-component/signal-component-io';
1819
import posthog from 'posthog-js';
1920
import { PlaceholderSavedFiltersComponent } from 'src/app/components/skeletons/placeholder-saved-filters/placeholder-saved-filters.component';
2021
import { UIwidgets as FilterUIwidgets, filterTypes } from 'src/app/consts/filter-types';
@@ -32,6 +33,7 @@ import { SavedFiltersDialogComponent } from './saved-filters-dialog/saved-filter
3233
CommonModule,
3334
FormsModule,
3435
DynamicModule,
36+
SignalComponentIoModule,
3537
MatButtonModule,
3638
MatIconModule,
3739
MatChipsModule,
Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
1+
import { Component, computed, input, OnInit, output } from '@angular/core';
22
import { TableField, TableForeignKey, WidgetStructure } from 'src/app/models/table';
3-
43
import { normalizeFieldName } from '../../../../lib/normalize';
54

65
@Component({
@@ -9,22 +8,20 @@ import { normalizeFieldName } from '../../../../lib/normalize';
98
styleUrl: './base-filter-field.component.css',
109
})
1110
export class BaseFilterFieldComponent implements OnInit {
12-
@Input() key: string;
13-
@Input() label: string;
14-
@Input() required: boolean;
15-
@Input() readonly: boolean;
16-
@Input() structure: TableField;
17-
@Input() disabled: boolean;
18-
@Input() widgetStructure: WidgetStructure;
19-
@Input() relations: TableForeignKey;
20-
@Input() autofocus: boolean = false;
11+
readonly key = input<string>();
12+
readonly label = input<string>();
13+
readonly required = input<boolean>(false);
14+
readonly readonly = input<boolean>(false);
15+
readonly structure = input<TableField>();
16+
readonly disabled = input<boolean>(false);
17+
readonly widgetStructure = input<WidgetStructure>();
18+
readonly relations = input<TableForeignKey>();
19+
readonly autofocus = input<boolean>(false);
2120

22-
@Output() onFieldChange = new EventEmitter<any>();
23-
@Output() onComparatorChange = new EventEmitter<string>();
21+
readonly onFieldChange = output<any>();
22+
readonly onComparatorChange = output<string>();
2423

25-
public normalizedLabel: string;
24+
readonly normalizedLabel = computed(() => normalizeFieldName(this.label() || ''));
2625

27-
ngOnInit(): void {
28-
this.normalizedLabel = normalizeFieldName(this.label);
29-
}
26+
ngOnInit(): void {}
3027
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<div class="static-text">
2-
<span>{{normalizedLabel}}</span>
2+
<span>{{normalizedLabel()}}</span>
33
<span class="static-text__value"
4-
attr.data-testid="record-{{label}}-binary-data-caption">
4+
attr.data-testid="record-{{label()}}-binary-data-caption">
55
binary data
66
</span>
7-
</div>
7+
</div>

frontend/src/app/components/ui-components/filter-fields/boolean/boolean.component.html

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<div *ngIf="isRadiogroup; else checkboxElement" class="radio-line">
2-
<label id="">{{normalizedLabel}}</label>
3-
<mat-button-toggle-group name="{{label}}-{{key}}" attr.data-testid="record-{{label}}-boolean-radio-group"
4-
[hideSingleSelectionIndicator]="true"
5-
[disabled]="disabled"
2+
<label id="">{{normalizedLabel()}}</label>
3+
<mat-button-toggle-group name="{{label()}}-{{key()}}" attr.data-testid="record-{{label()}}-boolean-radio-group"
4+
[hideSingleSelectionIndicator]="true"
5+
[disabled]="disabled()"
66
[(ngModel)]="booleanValue" (ngModelChange)="onBooleanChange()">
77
<mat-button-toggle [value]=true>Yes</mat-button-toggle>
88
<mat-button-toggle [value]=false>No</mat-button-toggle>
@@ -12,10 +12,10 @@
1212

1313
<ng-template #checkboxElement>
1414
<div class="radio-line">
15-
<label id="">{{normalizedLabel}}</label>
16-
<mat-button-toggle-group name="{{label}}-{{key}}" attr.data-testid="record-{{label}}-boolean-radio-group"
15+
<label id="">{{normalizedLabel()}}</label>
16+
<mat-button-toggle-group name="{{label()}}-{{key()}}" attr.data-testid="record-{{label()}}-boolean-radio-group"
1717
[hideSingleSelectionIndicator]="true"
18-
[disabled]="disabled"
18+
[disabled]="disabled()"
1919
[(ngModel)]="booleanValue" (ngModelChange)="onBooleanChange()">
2020
<mat-button-toggle [value]=true>Yes</mat-button-toggle>
2121
<mat-button-toggle [value]=false>No</mat-button-toggle>

frontend/src/app/components/ui-components/filter-fields/boolean/boolean.component.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,31 +38,31 @@ describe('BooleanFilterComponent', () => {
3838

3939
it('should set booleanValue in false when input value is 0', () => {
4040
component.value = 0;
41-
component.structure = fakeStructure;
41+
fixture.componentRef.setInput('structure', fakeStructure);
4242
component.ngOnInit();
4343

4444
expect(component.booleanValue).toEqual(false);
4545
});
4646

4747
it('should set booleanValue in unknown when input value is null', () => {
4848
component.value = null;
49-
component.structure = fakeStructure;
49+
fixture.componentRef.setInput('structure', fakeStructure);
5050
component.ngOnInit();
5151

5252
expect(component.booleanValue).toEqual('unknown');
5353
});
5454

5555
it('should set isRadiogroup in false if allow_null is false', () => {
5656
component.value = undefined;
57-
component.structure = fakeStructure;
57+
fixture.componentRef.setInput('structure', fakeStructure);
5858
component.ngOnInit();
5959

6060
expect(component.isRadiogroup).toEqual(false);
6161
});
6262

6363
it('should set isRadiogroup in true if allow_null is true', () => {
6464
component.value = undefined;
65-
component.structure = {
65+
fixture.componentRef.setInput('structure', {
6666
column_name: 'banned',
6767
column_default: '0',
6868
data_type: 'tinyint',
@@ -71,7 +71,7 @@ describe('BooleanFilterComponent', () => {
7171
auto_increment: false,
7272
allow_null: true,
7373
character_maximum_length: 1,
74-
};
74+
});
7575
component.ngOnInit();
7676

7777
expect(component.isRadiogroup).toEqual(true);

frontend/src/app/components/ui-components/filter-fields/boolean/boolean.component.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,16 @@ export class BooleanFilterComponent extends BaseFilterFieldComponent {
2424
}
2525

2626
ngOnInit(): void {
27-
super.ngOnInit();
2827
this.connectionType = this._connections.currentConnection.type;
2928
this.setBooleanValue();
3029

31-
// Parse widget parameters if available
3230
let parsedParams = null;
33-
if (this.widgetStructure?.widget_params) {
34-
parsedParams =
35-
typeof this.widgetStructure.widget_params === 'string'
36-
? JSON.parse(this.widgetStructure.widget_params)
37-
: this.widgetStructure.widget_params;
31+
const ws = this.widgetStructure();
32+
if (ws?.widget_params) {
33+
parsedParams = typeof ws.widget_params === 'string' ? JSON.parse(ws.widget_params) : ws.widget_params;
3834
}
3935

40-
// Check allow_null from either structure or widget params
41-
this.isRadiogroup = this.structure?.allow_null || !!parsedParams?.allow_null;
36+
this.isRadiogroup = this.structure()?.allow_null || !!parsedParams?.allow_null;
4237
}
4338

4439
setBooleanValue() {
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<mat-form-field class="country-form-field" appearance="outline">
2-
<mat-label>{{normalizedLabel}}</mat-label>
2+
<mat-label>{{normalizedLabel()}}</mat-label>
33
<input type="text" matInput
4-
[required]="required" [disabled]="disabled" [readonly]="readonly"
5-
attr.data-testid="record-{{label}}-country"
4+
[required]="required()" [disabled]="disabled()" [readonly]="readonly()"
5+
attr.data-testid="record-{{label()}}-country"
66
[formControl]="countryControl"
77
[matAutocomplete]="auto">
88
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
@@ -14,4 +14,4 @@
1414
<span *ngIf="country.value" class="country-code">({{country.value}})</span>
1515
</mat-option>
1616
</mat-autocomplete>
17-
</mat-form-field>
17+
</mat-form-field>

frontend/src/app/components/ui-components/filter-fields/country/country.component.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ export class CountryFilterComponent extends BaseFilterFieldComponent {
4242
getCountryFlag = getCountryFlag;
4343

4444
ngOnInit(): void {
45-
super.ngOnInit();
46-
47-
if (this.widgetStructure?.widget_params?.allow_null || this.structure?.allow_null) {
45+
const ws = this.widgetStructure();
46+
const struct = this.structure();
47+
if (ws?.widget_params?.allow_null || struct?.allow_null) {
4848
this.countries = [{ value: null, label: '', flag: '' }, ...BASE_COUNTRIES];
4949
} else {
5050
this.countries = BASE_COUNTRIES;

0 commit comments

Comments
 (0)