Skip to content

Commit 235776b

Browse files
authored
fix: Fix Many Requests After Using Back Button (#467)
* WIP * trigger also on change in prüfi input * put only the prüfidentifikator in the input field after selection * remove unused import * fix tests
1 parent fabce09 commit 235776b

File tree

6 files changed

+119
-77
lines changed

6 files changed

+119
-77
lines changed

src/app/features/ahbs/components/ahb-search-form-header/ahb-search-form-header.component.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,5 @@
33
<app-pruefi-input
44
class="inline-block h-16 w-64"
55
formControlName="pruefi"
6-
[formatVersion]="headerSearchForm.controls.formatVersion.value"
7-
(ngModelChange)="onPruefiSelect()" />
6+
[formatVersion]="headerSearchForm.controls.formatVersion.value" />
87
</form>

src/app/features/ahbs/components/ahb-search-form-header/ahb-search-form-header.component.ts

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, effect, input, output } from '@angular/core';
1+
import { Component, input, output, effect } from '@angular/core';
22
import {
33
FormControl,
44
FormGroup,
@@ -21,36 +21,51 @@ export class AhbSearchFormHeaderComponent {
2121
pruefi = input.required<string>();
2222

2323
formatVersionChange = output<string>();
24+
pruefiChange = output<string>();
2425

2526
headerSearchForm = new FormGroup({
2627
formatVersion: new FormControl('', Validators.required),
2728
pruefi: new FormControl('', Validators.required),
2829
});
2930

3031
constructor(private readonly router: Router) {
32+
// Update form when inputs change
3133
effect(() => {
32-
this.headerSearchForm.setValue({
33-
formatVersion: this.formatVersion(),
34-
pruefi: this.pruefi(),
35-
});
34+
const newFormatVersion = this.formatVersion();
35+
if (newFormatVersion !== this.headerSearchForm.get('formatVersion')?.value) {
36+
this.headerSearchForm.patchValue({ formatVersion: newFormatVersion }, { emitEvent: false });
37+
}
38+
});
39+
40+
effect(() => {
41+
const newPruefi = this.pruefi();
42+
if (newPruefi !== this.headerSearchForm.get('pruefi')?.value) {
43+
this.headerSearchForm.patchValue({ pruefi: newPruefi }, { emitEvent: false });
44+
}
3645
});
3746

47+
// Handle form control changes
3848
this.headerSearchForm.get('formatVersion')?.valueChanges.subscribe(value => {
3949
if (value) {
4050
this.formatVersionChange.emit(value);
4151
}
4252
});
53+
54+
this.headerSearchForm.get('pruefi')?.valueChanges.subscribe(value => {
55+
if (value) {
56+
this.pruefiChange.emit(value);
57+
this.navigateToAhb();
58+
}
59+
});
4360
}
4461

45-
onPruefiSelect() {
46-
if (!this.headerSearchForm.valid) {
47-
this.headerSearchForm.markAllAsTouched();
48-
return;
62+
private navigateToAhb() {
63+
if (this.headerSearchForm.valid) {
64+
this.router.navigate([
65+
'/ahb',
66+
this.headerSearchForm.value.formatVersion,
67+
this.headerSearchForm.value.pruefi,
68+
]);
4969
}
50-
this.router.navigate([
51-
'/ahb',
52-
this.headerSearchForm.value.formatVersion,
53-
this.headerSearchForm.value.pruefi,
54-
]);
5570
}
5671
}

src/app/features/ahbs/components/pruefi-input/pruefi-input.component.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,17 @@ export class PruefiInputComponent implements ControlValueAccessor {
112112
return;
113113
}
114114

115-
const regexPattern = /^(?<pruefi>\d{5})\b\s*-\s*(?<name>.+)\s*$/;
115+
const regexPattern = /^(?<pruefi>\d{5})\b(?:\s*-\s*(?<n>.+)\s*)?$/;
116116
const digitOnlyPattern = /^\d+$/;
117117

118118
if (regexPattern.test(inputValue)) {
119-
// Full format with name (e.g., "12345 - Some Name")
119+
// Full format with optional name (e.g., "12345 - Some Name" or just "12345")
120120
const match = inputValue.match(regexPattern);
121121
pruefidentifikator = match?.groups?.['pruefi'] ?? null;
122+
// Update the form control with just the pruefidentifikator
123+
if (pruefidentifikator) {
124+
this.control.setValue(pruefidentifikator, { emitEvent: false });
125+
}
122126
} else if (digitOnlyPattern.test(inputValue)) {
123127
// Handle numeric input
124128
if (inputValue.length === 5) {
@@ -127,14 +131,13 @@ export class PruefiInputComponent implements ControlValueAccessor {
127131
} else if (inputValue.length > 5) {
128132
// More than 5 digits - take first 5
129133
pruefidentifikator = inputValue.slice(0, 5);
134+
// Update the form control with just the first 5 digits
135+
this.control.setValue(pruefidentifikator, { emitEvent: false });
130136
}
131137
// Less than 5 digits - treat as invalid (pruefidentifikator remains null)
132138
}
133139
// Any other format is considered invalid (pruefidentifikator remains null)
134140

135-
// Update the form control with the full input value to allow searching
136-
this.control.setValue(inputValue);
137-
138141
// Emit changes
139142
if (this.onChange) {
140143
this.onChange(pruefidentifikator);

src/app/features/ahbs/views/ahb-page/ahb-page.component.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
class="flex-1 hidden md:inline"
66
[formatVersion]="formatVersion()"
77
[pruefi]="pruefi()"
8-
(formatVersionChange)="onFormatVersionChange($event)" />
8+
(formatVersionChange)="onFormatVersionChange($event)"
9+
(pruefiChange)="onPruefiChange($event)" />
910
<app-input-search-enhanced
1011
class="inline-block w-full md:w-96"
1112
[searchQuery]="searchQuery()"

src/app/features/ahbs/views/ahb-page/ahb-page.component.spec.ts

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,23 @@ import { signal, computed } from '@angular/core';
88

99
describe('AhbPageComponent', () => {
1010
let mockRouter: { navigate: jest.Mock };
11+
let mockAhbService: { getAhb$Json: jest.Mock };
1112

1213
beforeEach(() => {
1314
mockRouter = { navigate: jest.fn() };
15+
mockAhbService = {
16+
getAhb$Json: jest.fn(params =>
17+
of({
18+
meta: {
19+
pruefidentifikator: params.pruefi,
20+
description: '',
21+
direction: '',
22+
maus_version: '',
23+
},
24+
lines: [],
25+
})
26+
),
27+
};
1428

1529
return MockBuilder(AhbPageComponent)
1630
.keep(AhbTableComponent)
@@ -24,23 +38,21 @@ describe('AhbPageComponent', () => {
2438
resetMarkIndex: () => {},
2539
},
2640
})
27-
.mock(AhbService, {
28-
getAhb$Json: jest.fn(params =>
29-
of({
30-
meta: {
31-
pruefidentifikator: params.pruefi,
32-
description: '',
33-
direction: '',
34-
maus_version: '',
35-
},
36-
lines: [],
37-
})
38-
),
41+
.provide({
42+
provide: AhbService,
43+
useValue: mockAhbService,
3944
})
40-
.mock(ActivatedRoute, {
41-
queryParams: of({}),
45+
.provide({
46+
provide: ActivatedRoute,
47+
useValue: {
48+
queryParams: of({}),
49+
params: of({ formatVersion: 'FV123', pruefi: '123' }),
50+
},
4251
})
43-
.mock(Router, mockRouter);
52+
.provide({
53+
provide: Router,
54+
useValue: mockRouter,
55+
});
4456
});
4557

4658
it('should render', () => {
@@ -103,18 +115,16 @@ describe('AhbPageComponent', () => {
103115

104116
it('should refresh table and redirect URL upon formatversion change', () => {
105117
const fixture = MockRender(AhbPageComponent, {
106-
formatVersion: 'FV123',
107-
pruefi: '456',
118+
formatVersion: 'FV2304',
119+
pruefi: '123',
108120
});
109121
const component = fixture.point.componentInstance;
110122
const router = ngMocks.findInstance(Router);
111123

112124
const navigateSpy = jest.spyOn(router, 'navigate');
113125

114-
component.onFormatVersionChange('FV456');
126+
component.onFormatVersionChange('FV2410');
115127

116-
expect(navigateSpy).toHaveBeenCalledWith(['/ahb', 'FV456', '456']);
128+
expect(navigateSpy).toHaveBeenCalledWith(['/ahb', 'FV2410', '123']);
117129
});
118-
119-
// Add more tests here
120130
});

src/app/features/ahbs/views/ahb-page/ahb-page.component.ts

Lines changed: 48 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import {
22
Component,
3-
computed,
43
ElementRef,
5-
effect,
6-
input,
7-
signal,
84
viewChild,
95
OnInit,
6+
OnDestroy,
7+
signal,
8+
computed,
109
} from '@angular/core';
1110
import { HeaderComponent } from '../../../../shared/components/header/header.component';
1211
import { FooterComponent } from '../../../../shared/components/footer/footer.component';
@@ -16,7 +15,8 @@ import { AhbTableComponent } from '../../components/ahb-table/ahb-table.componen
1615
import { Ahb, AhbService } from '../../../../core/api';
1716
import { CommonModule } from '@angular/common';
1817
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
19-
import { Observable, map, shareReplay, tap, catchError, of } from 'rxjs';
18+
import { Observable, Subject, of } from 'rxjs';
19+
import { map, shareReplay, catchError, takeUntil, distinctUntilChanged } from 'rxjs/operators';
2020
import { AhbSearchFormHeaderComponent } from '../../components/ahb-search-form-header/ahb-search-form-header.component';
2121
import { InputSearchEnhancedComponent } from '../../../../shared/components/input-search-enhanced/input-search-enhanced.component';
2222
import { ExportButtonComponent } from '../../components/export-button/export-button.component';
@@ -42,63 +42,75 @@ import { FallbackPageComponent } from '../../../../shared/components/fallback-pa
4242
],
4343
templateUrl: './ahb-page.component.html',
4444
})
45-
export class AhbPageComponent implements OnInit {
46-
formatVersion = input.required<string>();
47-
pruefi = input.required<string>();
45+
export class AhbPageComponent implements OnInit, OnDestroy {
46+
// State management
47+
formatVersion = signal<string>('');
48+
pruefi = signal<string>('');
49+
searchQuery = signal<string | undefined>('');
50+
edifactFormat = computed(() => this.getEdifactFormat(this.pruefi()));
4851

52+
// View references
4953
table = viewChild(AhbTableComponent);
5054
scroll = viewChild<ElementRef>('scroll');
5155

52-
searchQuery = signal<string | undefined>('');
53-
edifactFormat = computed(() => this.getEdifactFormat(this.pruefi()));
54-
56+
// Data streams
5557
ahb$?: Observable<Ahb>;
5658
lines$?: Observable<Ahb['lines']>;
5759
errorOccurred = false;
5860

59-
private initialSearchQuery: string | null = null;
61+
private destroy$ = new Subject<void>();
6062

6163
constructor(
6264
private readonly ahbService: AhbService,
6365
private readonly route: ActivatedRoute,
6466
private readonly router: Router
65-
) {
66-
effect(() => {
67-
this.loadAhbData();
68-
});
69-
}
67+
) {}
7068

7169
ngOnInit() {
72-
this.route.queryParams.subscribe(params => {
73-
const query = params['query'];
74-
if (query) {
75-
this.initialSearchQuery = query;
76-
this.searchQuery.set(query);
70+
// Handle route parameters
71+
this.route.params.pipe(takeUntil(this.destroy$), distinctUntilChanged()).subscribe(params => {
72+
const formatVersion = params['formatVersion'];
73+
const pruefi = params['pruefi'];
74+
75+
if (formatVersion && pruefi) {
76+
this.formatVersion.set(formatVersion);
77+
this.pruefi.set(pruefi);
78+
this.loadAhbData(formatVersion, pruefi);
7779
}
7880
});
81+
82+
// Handle search query params
83+
this.route.queryParams
84+
.pipe(takeUntil(this.destroy$), distinctUntilChanged())
85+
.subscribe(params => {
86+
const query = params['query'];
87+
if (query) {
88+
this.searchQuery.set(query);
89+
this.triggerSearch(query);
90+
}
91+
});
92+
}
93+
94+
ngOnDestroy() {
95+
this.destroy$.next();
96+
this.destroy$.complete();
7997
}
8098

81-
private loadAhbData() {
99+
private loadAhbData(formatVersion: string, pruefi: string) {
82100
this.errorOccurred = false;
83101

84102
this.ahb$ = this.ahbService
85103
.getAhb$Json({
86-
'format-version': this.formatVersion(),
87-
pruefi: this.pruefi(),
104+
'format-version': formatVersion,
105+
pruefi: pruefi,
88106
})
89107
.pipe(
90-
tap(() => {
91-
if (this.initialSearchQuery) {
92-
setTimeout(() => this.triggerSearch(this.initialSearchQuery!), 0);
93-
}
94-
}),
95108
shareReplay(1),
96109
catchError(error => {
97110
if (error.status === 404) {
98111
this.errorOccurred = true;
99112
}
100-
// Return an empty object of type Ahb if there's an error
101-
return of({} as Ahb); // Returning a fallback object of type Ahb
113+
return of({} as Ahb);
102114
})
103115
);
104116

@@ -109,6 +121,10 @@ export class AhbPageComponent implements OnInit {
109121
this.router.navigate(['/ahb', newFormatVersion, this.pruefi()]);
110122
}
111123

124+
onPruefiChange(newPruefi: string) {
125+
this.router.navigate(['/ahb', this.formatVersion(), newPruefi]);
126+
}
127+
112128
triggerSearch(query: string | undefined) {
113129
if (!query) return;
114130

@@ -118,7 +134,6 @@ export class AhbPageComponent implements OnInit {
118134
tableComponent.resetMarkIndex();
119135
}, 0);
120136
}
121-
this.initialSearchQuery = null; // Reset after first use
122137
}
123138

124139
scrollToElement(element: HTMLElement, offsetY: number): void {
@@ -155,7 +170,6 @@ export class AhbPageComponent implements OnInit {
155170
}
156171
}
157172

158-
// mapping provided by mig_ahb_utility_stack
159173
private getEdifactFormat(pruefi: string): string {
160174
const mapping: { [key: string]: string } = {
161175
'99': 'APERAK',

0 commit comments

Comments
 (0)