Skip to content

Commit 85e9de0

Browse files
author
Matus Kasak
committed
Different approach to link ids in submission form
1 parent 1b769b9 commit 85e9de0

File tree

11 files changed

+60
-16
lines changed

11 files changed

+60
-16
lines changed

src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[formGroup]="group"
44
[ngClass]="[getClass('element', 'container'), getClass('grid', 'container')]">
55
<label *ngIf="!isCheckbox && hasLabel"
6-
[id]="'label_' + model.id"
6+
[id]="'label_' + id"
77
[for]="id"
88
[innerHTML]="(model.required && model.label) ? (model.label | translate) + ' *' : (model.label | translate)"
99
[ngClass]="[getClass('element', 'label'), getClass('grid', 'label')]"></label>

src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,11 +253,36 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
253253
* Determines whether to request embedded thumbnail.
254254
*/
255255
fetchThumbnail: boolean;
256+
257+
/**
258+
* Unique suffix for generating unique IDs for this container instance
259+
*/
260+
private uniqueSuffix: string;
256261

257262
get componentType(): Type<DynamicFormControl> | null {
258263
return dsDynamicFormControlMapFn(this.model);
259264
}
260265

266+
/**
267+
* Get unique ID for this form control
268+
* Appends unique suffix to avoid ID collisions in repeatable form arrays
269+
*/
270+
get id(): string {
271+
if (this.bindId && this.model) {
272+
const baseId = this.model.id;
273+
// Check if we're in a form array context
274+
if (this.context && this.context instanceof DynamicFormArrayGroupModel && hasValue(this.context.index)) {
275+
return `${baseId}_${this.context.index}`;
276+
}
277+
// For components that don't properly receive context, use unique suffix
278+
if (!this.uniqueSuffix) {
279+
this.uniqueSuffix = Math.random().toString(36).substr(2, 9);
280+
}
281+
return `${baseId}_${this.uniqueSuffix}`;
282+
}
283+
return this.model?.id || '';
284+
}
285+
261286
constructor(
262287
protected componentFactoryResolver: ComponentFactoryResolver,
263288
protected dynamicFormComponentService: DynamicFormComponentService,

src/app/shared/form/builder/ds-dynamic-form-ui/models/autocomplete/ds-dynamic-autocomplete.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<label *ngIf="searching" class="fas fa-circle-notch fa-spin fa-2x fa-fw text-primary position-absolute mt-1 p-0" aria-hidden="true"></label>
66
<input class="form-control"
77
type="text"
8-
[attr.aria-labelledby]="'label_' + model.id"
8+
[attr.aria-labelledby]="'label_' + id"
99
[(ngModel)]="currentValue"
1010
[attr.autoComplete]="model.autoComplete"
1111
[class.is-invalid]="showErrorMessages"

src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker-inline/dynamic-date-picker-inline.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<div [formGroup]="group" class="input-group">
22

33
<input ngbDatepicker class="form-control" #datepicker="ngbDatepicker"
4-
[attr.aria-labelledby]="'label_' + model.id"
4+
[attr.aria-labelledby]="'label_' + id"
55
[class.is-invalid]="showErrorMessages"
66
[displayMonths]="model.getAdditional('displayMonths', config['displayMonths'])"
77
[id]="id"
@@ -27,7 +27,7 @@
2727

2828
<button class="btn btn-outline-secondary"
2929
type="button"
30-
[attr.aria-labelledby]="'label_' + model.id"
30+
[attr.aria-labelledby]="'label_' + id"
3131
[class.disabled]="model.disabled"
3232
[dsBtnDisabled]="model.disabled"
3333
(click)="datepicker.toggle()">

src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<div *ngFor="let item of columnItems" class="custom-control custom-checkbox">
1212

1313
<input type="checkbox" class="custom-control-input"
14-
[attr.aria-labelledby]="'label_' + model.id"
14+
[attr.aria-labelledby]="'label_' + id"
1515
[attr.tabindex]="item.index"
1616
[checked]="item.value"
1717
[id]="item.id"

src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
[authorityValue]="model.value"
1212
(whenClickOnConfidenceNotAccepted)="whenClickOnConfidenceNotAccepted(sdRef, $event)"></i>
1313
<input class="form-control"
14-
[attr.aria-labelledby]="'label_' + model.id"
14+
[attr.aria-labelledby]="'label_' + id"
1515
[attr.autoComplete]="model.autoComplete"
1616
[class.is-invalid]="showErrorMessages"
1717
[id]="model.id"
@@ -31,7 +31,7 @@
3131
<div *ngIf="isLookupName()" class="col" >
3232
<input class="form-control"
3333
[ngClass]="{}"
34-
[attr.aria-labelledby]="'label_' + model.id"
34+
[attr.aria-labelledby]="'label_' + id"
3535
[attr.autoComplete]="model.autoComplete"
3636
[class.is-invalid]="showErrorMessages"
3737
[id]="id"
@@ -86,7 +86,7 @@
8686
class="dropdown-menu scrollable-dropdown-menu w-100"
8787
aria-haspopup="true"
8888
aria-expanded="false"
89-
[attr.aria-labelledby]="'label_' + model.id">
89+
[attr.aria-labelledby]="'label_' + id">
9090
<div class="scrollable-menu"
9191
infiniteScroll
9292
[infiniteScrollDistance]="2"
@@ -113,7 +113,7 @@
113113
</div>
114114

115115
<ng-template #hasInfo let-entry="entry">
116-
<ul class="list-unstyled mb-0" [attr.aria-labelledby]="'label_' + model.id">
116+
<ul class="list-unstyled mb-0" [attr.aria-labelledby]="'label_' + id">
117117
<li class="list-item text-truncate text-primary font-weight-bold">{{entry.value}}</li>
118118
<li class="list-item text-truncate text-secondary" *ngFor="let item of entry.otherInformation | dsObjNgFor" >
119119
{{ 'form.other-information.' + item.key | translate }} : {{item.value}}
@@ -122,7 +122,7 @@
122122
</ng-template>
123123

124124
<ng-template #noInfo let-entry="entry">
125-
<ul class="list-unstyled mb-0" [attr.aria-labelledby]="'label_' + model.id">
125+
<ul class="list-unstyled mb-0" [attr.aria-labelledby]="'label_' + id">
126126
<li class="list-item text-truncate text-primary font-weight-bold">{{entry.value}}</li>
127127
</ul>
128128
</ng-template>

src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
(whenClickOnConfidenceNotAccepted)="whenClickOnConfidenceNotAccepted($event)"></i>
3131
<input #instance="ngbTypeahead"
3232
class="form-control"
33-
[attr.aria-labelledby]="'label_' + model.id"
33+
[attr.aria-labelledby]="'label_' + id"
3434
[attr.autoComplete]="model.autoComplete"
3535
[class.is-invalid]="showErrorMessages"
3636
[id]="model.id"
@@ -56,7 +56,7 @@
5656
<i class="dropdown-toggle position-absolute tree-toggle" (click)="openTree($event)"
5757
aria-hidden="true"></i>
5858
<input class="form-control"
59-
[attr.aria-labelledby]="'label_' + model.id"
59+
[attr.aria-labelledby]="'label_' + id"
6060
[attr.autoComplete]="model.autoComplete"
6161
[class.is-invalid]="showErrorMessages"
6262
[class.tree-input]="!model.readOnly"

src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { UntypedFormGroup } from '@angular/forms';
1313
import { Observable, of as observableOf } from 'rxjs';
1414
import { catchError, distinctUntilChanged, map, tap } from 'rxjs/operators';
1515
import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap';
16-
import { DynamicFormLayoutService, DynamicFormValidationService } from '@ng-dynamic-forms/core';
16+
import { DynamicFormArrayGroupModel, DynamicFormLayoutService, DynamicFormValidationService } from '@ng-dynamic-forms/core';
1717

1818
import { DynamicScrollableDropdownModel } from './dynamic-scrollable-dropdown.model';
1919
import { PageInfo } from '../../../../../../core/shared/page-info.model';
@@ -44,6 +44,7 @@ export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyCom
4444
@Input() bindId = true;
4545
@Input() group: UntypedFormGroup;
4646
@Input() model: DynamicScrollableDropdownModel;
47+
@Input() context: any | null = null;
4748

4849
@Output() blur: EventEmitter<any> = new EventEmitter<any>();
4950
@Output() change: EventEmitter<any> = new EventEmitter<any>();
@@ -56,6 +57,7 @@ export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyCom
5657
public inputText: string = null;
5758
public selectedIndex = 0;
5859
public acceptableKeys = ['Space', 'NumpadMultiply', 'NumpadAdd', 'NumpadSubtract', 'NumpadDecimal', 'Semicolon', 'Equal', 'Comma', 'Minus', 'Period', 'Quote', 'Backquote'];
60+
private uniqueSuffix: string;
5961

6062
/**
6163
* If true the component can rely on the findAll method for data loading.
@@ -78,6 +80,23 @@ export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyCom
7880
protected validationService: DynamicFormValidationService
7981
) {
8082
super(vocabularyService, layoutService, validationService);
83+
// Generate a unique suffix for this component instance to avoid ID collisions
84+
this.uniqueSuffix = Math.random().toString(36).substr(2, 9);
85+
}
86+
87+
/**
88+
* Get unique ID for this form control
89+
*/
90+
get id(): string {
91+
const baseId = this.bindId ? this.model.id : this.model.id;
92+
93+
// Try to get index from context if available
94+
if (this.context && this.context instanceof DynamicFormArrayGroupModel && hasValue(this.context.index)) {
95+
return `${baseId}_${this.context.index}`;
96+
}
97+
98+
// Use the unique suffix to ensure uniqueness
99+
return `${baseId}_${this.uniqueSuffix}`;
81100
}
82101

83102
/**

src/app/shared/form/builder/ds-dynamic-form-ui/models/sponsor-autocomplete/ds-dynamic-sponsor-autocomplete.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<label *ngIf="searching" class="fas fa-circle-notch fa-spin fa-2x fa-fw text-primary position-absolute mt-1 p-0" aria-hidden="true"></label>
66
<input class="form-control"
77
type="text"
8-
[attr.aria-labelledby]="'label_' + model.id"
8+
[attr.aria-labelledby]="'label_' + id"
99
[(ngModel)]="currentValue"
1010
[attr.autoComplete]="model.autoComplete"
1111
[class.is-invalid]="showErrorMessages"

src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
<i *ngIf="searching" class="fas fa-circle-notch fa-spin fa-2x fa-fw text-primary position-absolute mt-1 p-0" aria-hidden="true"></i>
2626
<input class="border-0 form-control-plaintext tag-input flex-grow-1 mt-1 mb-1"
2727
type="text"
28-
[attr.aria-labelledby]="'label_' + model.id"
28+
[attr.aria-labelledby]="'label_' + id"
2929
[(ngModel)]="currentValue"
3030
[attr.autoComplete]="model.autoComplete"
3131
[class.is-invalid]="showErrorMessages"

0 commit comments

Comments
 (0)