Symptom
Uploading a picture fails with the toast "Sagen blev ikke fundet" (CaseNotFound) whenever an eForm is filled inside a MatDialog rather than on a routed page. Reported against the Backend Configuration calendar's complete-event modal, but the defect lives in the shared eform-cases chain and affects any dialog consumer.
Root cause
eform-client/src/app/common/modules/eform-cases/components/case-edit/case-elements/element-picture/element-picture.component.ts obtains the case id only from the router URL (in ngOnChanges, ~line 51):
this.activatedRouteSub$ = this.activateRoute.params.subscribe((params) => {
this.caseId = +params['id'];
if (isNaN(this.caseId)) {
this.caseId = +params['sdkCaseId'];
}
});
There is no @Input() caseId — the component only declares @Input() fieldValues and @Input() fieldId, with caseId as a plain field. Inside a dialog, the injected ActivatedRoute resolves to the route the dialog was opened from. For a route that declares neither :id nor :sdkCaseId, both lookups yield +undefined → NaN.
TemplateFilesService.addNewImage (eform-client/src/app/common/services/cases/template-files.service.ts:61) then posts that through ApiBaseService.postFormData → objectToFormData(body, true), which pascal-cases the keys and appends the literal string "CaseId=NaN". EFormFilesController.AddNewImage (eFormAPI/eFormAPI.Web/Controllers/Eforms/EFormFilesController.cs:261) is a plain Controller (no [ApiController], and no global invalid-ModelState filter in Startup.cs — AddMvc only sets EnableEndpointRouting and MaxModelBindingCollectionSize), so "NaN" silently binds to 0, Cases.Where(x => x.Id == caseId) returns null, and the action returns the localized CaseNotFound — Danish "Sagen blev ikke fundet" (eFormAPI/eFormAPI.Web/Resources/SharedResource.da.resx:623).
Every working consumer is a routed page that happens to carry the id in its URL:
| Consumer |
Route |
param |
modules/cases/components/case-edit |
edit/:id/:templateId |
id |
compliance/…/compliance-case-page |
:sdkCaseId/:templateId/:propertyId/… |
sdkCaseId |
backend-configuration-case-page |
:id/:templateId/:planningId |
id |
| any dialog consumer |
— |
none → NaN |
The params['sdkCaseId'] fallback in the current code exists precisely because one routed consumer names the param differently — which is a good hint that URL-sniffing was already the wrong mechanism.
Fix
Thread the case id down the chain as an optional input, keeping the route lookup as a fallback so the three routed consumers above are behaviourally untouched.
CaseEditElementComponent — add @Input() caseId?: number; forward it to <app-case-edit-switch> in case-edit-element.component.html (currently line 19-22, binds only [dataItemList]).
CaseEditSwitchComponent — add @Input() caseId?: number; forward it to <element-picture> in case-edit-switch.component.html (line 21).
ElementPictureComponent — add @Input() caseId?: number. Resolve input first, route param as fallback. Keep the existing params['id'] → params['sdkCaseId'] logic intact for the fallback branch.
Also fix on the same chain
case-edit-element.component.html (lines 34-37) renders the "Extra picture" slot as
<element-picture
(pictureUpdated)="emitNeedUpdate()"
[fieldValues]="element.extraPictures"
></element-picture>
with no [fieldId]. Bind the new [caseId] there.
Correction (during implementation): do NOT bind [fieldId] here. Investigation showed extra pictures are ExtraFieldValue rows (eform-sdk/.../ExtraFieldValue.cs), keyed by CaseId + CheckListId + FieldTypeId with no FieldId column, and SqlController.cs:1817-1826 never populates FieldId on the way out — so every entry serializes as fieldId: 0. Binding that would be actively harmful: element-picture.component.html:1 gates the card on *ngIf="fieldId != 0", so the entire extra-picture card would disappear along with already-uploaded thumbnails. Extra-picture upload cannot be fixed from the frontend at all — it needs a server path that writes an ExtraFieldValue rather than a FieldValue. Split out to #8032.
Scope / compatibility
Purely additive — the new inputs are optional and no existing consumer is forced to change. No API, base or SDK change. No migration.
Downstream
Plugin CI pins the frontend to stable, so this PR must merge before the Backend Configuration plugin change that binds [caseId] — otherwise the plugin build fails to compile, not merely to test. Tracked in microting/eform-backendconfiguration-plugin.
Acceptance
Symptom
Uploading a picture fails with the toast "Sagen blev ikke fundet" (
CaseNotFound) whenever an eForm is filled inside a MatDialog rather than on a routed page. Reported against the Backend Configuration calendar's complete-event modal, but the defect lives in the sharedeform-caseschain and affects any dialog consumer.Root cause
eform-client/src/app/common/modules/eform-cases/components/case-edit/case-elements/element-picture/element-picture.component.tsobtains the case id only from the router URL (inngOnChanges, ~line 51):There is no
@Input() caseId— the component only declares@Input() fieldValuesand@Input() fieldId, withcaseIdas a plain field. Inside a dialog, the injectedActivatedRouteresolves to the route the dialog was opened from. For a route that declares neither:idnor:sdkCaseId, both lookups yield+undefined→NaN.TemplateFilesService.addNewImage(eform-client/src/app/common/services/cases/template-files.service.ts:61) then posts that throughApiBaseService.postFormData→objectToFormData(body, true), which pascal-cases the keys and appends the literal string"CaseId=NaN".EFormFilesController.AddNewImage(eFormAPI/eFormAPI.Web/Controllers/Eforms/EFormFilesController.cs:261) is a plainController(no[ApiController], and no global invalid-ModelState filter inStartup.cs—AddMvconly setsEnableEndpointRoutingandMaxModelBindingCollectionSize), so"NaN"silently binds to0,Cases.Where(x => x.Id == caseId)returns null, and the action returns the localizedCaseNotFound— Danish"Sagen blev ikke fundet"(eFormAPI/eFormAPI.Web/Resources/SharedResource.da.resx:623).Every working consumer is a routed page that happens to carry the id in its URL:
modules/cases/components/case-editedit/:id/:templateIdidcompliance/…/compliance-case-page:sdkCaseId/:templateId/:propertyId/…sdkCaseIdbackend-configuration-case-page:id/:templateId/:planningIdidThe
params['sdkCaseId']fallback in the current code exists precisely because one routed consumer names the param differently — which is a good hint that URL-sniffing was already the wrong mechanism.Fix
Thread the case id down the chain as an optional input, keeping the route lookup as a fallback so the three routed consumers above are behaviourally untouched.
CaseEditElementComponent— add@Input() caseId?: number; forward it to<app-case-edit-switch>incase-edit-element.component.html(currently line 19-22, binds only[dataItemList]).CaseEditSwitchComponent— add@Input() caseId?: number; forward it to<element-picture>incase-edit-switch.component.html(line 21).ElementPictureComponent— add@Input() caseId?: number. Resolve input first, route param as fallback. Keep the existingparams['id']→params['sdkCaseId']logic intact for the fallback branch.Also fix on the same chain
case-edit-element.component.html(lines 34-37) renders the "Extra picture" slot aswith no
[fieldId]. Bind the new[caseId]there.Scope / compatibility
Purely additive — the new inputs are optional and no existing consumer is forced to change. No API, base or SDK change. No migration.
Downstream
Plugin CI pins the frontend to
stable, so this PR must merge before the Backend Configuration plugin change that binds[caseId]— otherwise the plugin build fails to compile, not merely to test. Tracked inmicroting/eform-backendconfiguration-plugin.Acceptance
[caseId]binding is additive and must not change existing rendering). Actually making extra-picture upload work is out of scope — see Extra-picture upload can never succeed — the slot is ExtraFieldValue-shaped but AddNewImage is FieldValue-shaped #8032.