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 tofalse
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 namedata.name
= suggested download filename