Attachments
Uploading and downloading files attached to an entity — a front-end concern layered on top of the basic CRUD slice.
The shape
Attachments are themselves a small entity (entity-attachments), referenced as a child collection on the host model. The host carries an optional array; soft-deleted rows are dropped before save:
import type { Entity as EntityAttachment } from "../../entity-attachments"
class Vehicle extends EntityBase {
attachments?: Array<EntityAttachment>
// …
}A file is a Blob; an attachment wraps one. The entity-attachments module exports the helpers that turn files into savable attachments and round-trip them with the host (createEntity, useEntityAttachments, insertWithAttachments, updateWithAttachments, download) plus the reusable Overview component (imported as EntityAttachments) and the model Entity (imported as EntityAttachment).
The attachment service
A host that owns files uses the "with attachments" service variant: it overrides insert / update to round-trip files alongside the record. setup.ts needs nothing — the helpers upload through useAxios(), so the service keeps its plain AxiosInstance constructor and the default registration stands. The shape:
export class EntityService extends EntityServiceBase<Entity> {
constructor(axios: AxiosInstance, config: IConfig) {
super(axios, config)
}
override insert(item) {
// the follow-up update callback sends the attachments in display order — the server assigns SortOrder from array position
return insertWithAttachments(
this.config.api,
item,
() => super.insert(item),
(saved) => super.update(saved)
)
}
override update(item) {
return updateWithAttachments(this.config.api, item, () => super.update(item))
}
}It is only when you add file endpoints of your own — e.g. the advanced example's optional getAttachments(so?) (GET {api}/attachments) and addAttachment(itemId, file) (POST {api}/{id}/files), which call this.axios.upload / getFile — that the constructor takes an AxiosWithFilesInstance (the file-aware axios from vue/http) and setup.ts has to resolve one.
The base prepareItem only strips top-level _-prefixed keys — it does not filter soft-deleted children, so override it to drop _deleted attachments (and other owned rows) before save — see services.md for prepareItem / processItem.
Upload / download UI — file fields
In the form, a dedicated files tab binds the host's attachments array to the shared EntityAttachments overview (imported from entity-attachments):
<EntityAttachments v-model="item.attachments" :readonly="readonly" />That component owns the add/remove/preview interactions; you do not hand-roll the file <input>. Wire it into a tab alongside the main form.
Download URLs
Downloads go through the file-aware axios from vue/http, not a bare <a href>: AxiosWithFilesInstance exposes getFile(url, method?, filename?, type?) (returns a Blob) and upload(url, files, options?). Resolve the instance with useAxios() and build the URL from config.api (e.g. {api}/{id}/files).
When you need it
Only complex entities that own files. The simple/standard slices have none of the above — no file fields and the default insert/update. Reach for the attachments variant only when the entity actually carries files, and keep everything else identical to those slices.