Working with files and images

Prev Next

Store a Single Image in an Aggregate

When to use:
Choose this approach when the aggregate requires exactly one image field that is tied to a specific business purpose, such as:

  • An equipment photo in an asset record

  • A user profile picture in an account

  • A company logo in an organization profile

This is useful when the image:

  • Has its own dedicated update command and access permissions

  • Must always represent the same type of visual (not an arbitrary collection of images)

  • Should be stored, displayed, and managed as a single value rather than part of a gallery or list

How to:

1. Define the image field and update command

  • Add a field of type Image to the aggregate.

  • Create a command (e.g., updateImage).

  • Add the image field as a command parameter.


2. For in-place viewing and editing

  • Drag and drop the image field onto the screen.

  • Select the “Value only” display option.

  • Click the image component to review its configuration.

  • Remove the update command from the configuration if you want the image to be read-only.

  • If the update command is kept:

    • Clicking the image will prompt the user to browse for a new file if none is set.

    • If an image is already set, clicking will clear it.


3. To use a dialog for editing

  • Drag and drop the image field onto the screen.

  • Select the “Value and label” display option.

  • Click the field component to review its configuration.

  • Clicking the field opens a standard action dialog.

    • The dialog can be configured to allow clearing the image (if the parameter is not marked as required).

    • The dialog can also allow selecting a new image.

    • You can add additional parameters to the update command for more complex update logic.

Store a Single File in an Aggregate

When to use:
Use this pattern when an aggregate needs to store one specific, named file that has its own update command and access permissions. This is ideal when:

  • The file serves a dedicated business purpose and is always the same type of document.

  • It must be managed independently from other files in the system.

  • Access rights and update rules differ from other documents in the aggregate.

Example:
An Employee record might store three separate documents—employmentContract, jobDescription, and signedNDA—each requiring its own permissions and update logic. Instead of treating them as a single collection of attachments, define each as an individual file field.


How to:

1. Define the file field and update command

  • Add a field of type File to the aggregate.

  • Create a command (e.g., updateFile).

  • Add the file field as a parameter to that command.


2. Add the field to the screen

  • Drag and drop the file field onto the screen.

  • Select the “Value and label” display option.

  • Click the field component to review its configuration.


3. Configure file interaction

  • Clicking the field will open a standard action dialog.

    • The dialog allows the user to clear the file (if the parameter is not marked as required).

    • The dialog allows the user to browse and select a new file.

    • You can add extra parameters to the update command for more complex update logic.

Store a Collection of Files in an Aggregate

When to use:
Use this pattern when an aggregate needs to store and manage multiple related files of the same type as a set, rather than as separate named fields. This is ideal when:

  • All files share the same update command and permissions.

  • Files are added, displayed, and removed from one unified list.

  • The number of files can change over time.

Example:
A Project record might store reference images, design drafts, or supporting documents that all follow the same rules. Instead of creating one field per file, store them together in a single file collection field for simpler management.


1. Define the collection field and update command

  • Add a field of type File to the aggregate.

  • Mark it as a collection.

  • Create a command (e.g., updateFiles).

  • Add the file collection field as a command parameter.


2. Easiest option — automatic display with built-in file management

  • Drag and drop the command onto the screen and select “Automatic form”.

  • Remove the Submit and Cancel buttons.

  • In the Page Structure panel, select the box that has the Create a Command Form behavior and enable auto-save.

Result:
This automatically renders a file list that allows:

  • Uploading new files

  • Deleting existing files

  • Downloading files by clicking them

No extra wiring or custom logic is needed.


3. Advanced — create a custom display

  • Drag and drop the file field onto the screen.

  • Choose Repeater as the display type.

  • Drag and drop the command and select a Button type.

  • Activate Play mode, click the button, and select one or more files to upload.

  • Deactivate Play mode.

  • Style the repeater to match your layout.

  • Bind the click event on repeater items to execute the update command.


4. Support deletion of individual files (custom approach)

Create the delete command:

  • Create a deleteFile command.

  • Add a storageId parameter (type: Text).

  • Create a new mutation field targeting the collection field.

  • In the mutation field, use:

return data.files.reject({ storageId }).add({ $replace: true })

Wire up the UI:

  • In the repeater, add an Add Dependency behavior before Set Component Data:

    • Name: parentData

    • Value: data

  • Add a trash icon to each repeater card.

  • On the trash icon’s onClick event, add a Show Custom Dialog behavior.

  • In the dialog’s On submit, run:

parentData.removeFile({ storageId: data.storageId })

Store a Collection of Files in an Aggregate where each file has domain-specific fields

When to use

Choose this when each file is essentially an entity with its own data and lifecycle, not just an attachment. Typical needs:

  • Custom metadata (e.g., owner, status, tags)

  • Domain commands (e.g., Approve, Reject)

  • Workflows/lifecycle (e.g., Draft → Approved → Archived)

Example:
A Project has a documents collection of Document entities. Each Document has a single file (content), plus fields like owner and status, and commands such as Approve / Reject.


Define the collection field and entity in the aggregate

1. Add the collection to the aggregate

  • Create a new field on the aggregate (e.g., documents).

  • Set its type to New Entity.

  • When prompted, name the entity Document.

  • The UI links documents (collection) ↔ Document (entity).

2. Add fields to the new entity

  • In Document, add a File field (e.g., content).

  • Add any domain fields you need (e.g., owner, status, tags).

3. Create the update command on the parent

  • On the aggregate, create updateDocuments (regular command, not “Entity Create”).

  • Add the documents collection as a parameter, make sure the parameter is set as a collection (Is Collection).

  • Set the mutation for that parameter to Replace values.

4. Create the delete command on the entity

  • On Document, add a standard Delete command.

5. Configure the upload dialog (on updateDocuments)

  • Add a Dialog declaration.

  • Configure the documents field:

    componentId: @skyjs/data-model/web/DocumentsInput
    props: { "hidePreview": true, "fieldName": "content" }
    Hide label : true
    

    fieldName must match the entity’s single File field.

6. Set default values when adding new files

  • In the parameter’s onChange property, use:

    return newValue => newValue.assignNewDocumentsValues(
      { uploadDate: new DateTime(), owner: currentUser, status: 'DRAFT' },
      data
    );

    This code is your chance to set default values for your custom entity when new files are added.  You could, for example, set the current user as the owner, initialize a status, or apply any other default metadata relevant to your domain.


UI — Automatic file list via command form

  • Drag updateDocuments onto the screen → choose Manual form.

  • Remove Submit and Cancel buttons.

  • In Page Structure, select the box with Create a Command Form → enable auto-save.

Result:
A working list that supports upload, delete, and download.

With "hidePreview": true, selected files won’t show the default preview.
Set it to false for filename/size, or build a custom view (below) to show domain fields and actions.


UI — Custom document cards in a Repeater

  • Drag the documents collection onto the screen → choose Repeater.

  • Drag updateDocuments onto the page → choose a Button type.

  • In Play mode, click the button and select one or more files; exit Play mode.

  • Style the repeater.

  • Inside each card:

    • Add the Delete command (button/icon/link).

    • Add your domain commands (e.g., Approve / Reject).

    • Display fields like owner, status, tags, uploadDate.

  • To allow downloading the file, attach to a filename component or download icon a Execute JavaScript behavior:

window.location = await getFileUrl("get", data.content.storageId, data.name)
  • data.content = your content field name

  • data.name = suggested download filename