Skip to content

fix(eform-cases): picture upload is broken whenever a case is edited inside a dialog — element-picture reads its case id from the URL #8031

Description

@renemadsen

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 +undefinedNaN.

TemplateFilesService.addNewImage (eform-client/src/app/common/services/cases/template-files.service.ts:61) then posts that through ApiBaseService.postFormDataobjectToFormData(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.csAddMvc 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.

  1. 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]).
  2. CaseEditSwitchComponent — add @Input() caseId?: number; forward it to <element-picture> in case-edit-switch.component.html (line 21).
  3. 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions