useCwaResourceUpload
Use in admin tab components for uploadable components. Not for display components.
The composable returns a ready-to-spread bind object — spread it onto CwaUiFormFile with v-bind and it wires the v-model, fileExists, disabled, and the change/delete events for you. Only label (and optionally accept) stay per-field:
import { useCwaResourceManagerTab, useCwaResourceUpload } from '#imports'
const { exposeMeta, iri } = useCwaResourceManagerTab({ name: 'Upload' })
const { bind } = useCwaResourceUpload(iri)
defineExpose(exposeMeta)
<template>
<CwaUiFormFile v-bind="bind" label="Upload Image" />
</template>
bind. It's a ComputedRef, and Vue only auto-unwraps refs that are top-level setup bindings. So v-bind="bind" unwraps correctly, but v-bind="upload.bind" (nested access on the composable-return object) does not — it spreads the raw ref and TypeScript complains that modelValue/fileExists are missing. Destructure const { bind } = useCwaResourceUpload(iri), and for multiple fields destructure-and-rename per call (see below).Signature
useCwaResourceUpload(
iri: ComputedRef<string | undefined>,
filename: string = 'file',
fileDisplayType: string = 'File'
)
iri— the reactive IRI ref fromuseCwaResourceManagerTab. Identifies which resource to PATCH.filename— the PHP entity property name / media object key for the file field. Defaults to'file'. Pass the actual property name if your entity uses a different name (e.g.'image','document').fileDisplayType— noun used in the delete-confirmation dialog copy (e.g.'Image','Document'). Defaults to'File'.
Return values
| Return | Type | Purpose |
|---|---|---|
bind | ComputedRef<object> | Spread onto CwaUiFormFile via v-bind — covers v-model, fileExists, disabled, change, delete |
filenameInputModel | Ref<string> | Current filename (the v-model target inside bind) |
updating | Ref<boolean> | true while an upload/delete is in progress |
fileExists | ComputedRef<boolean> | true when a file is already uploaded for this resource |
handleInputChangeFile | (file: File | undefined) => Promise<void> | Change handler (also inside bind as onChange) |
handleInputDeleteFile | () => Promise<void> | Delete handler (also inside bind as onDelete) |
bind is the recommended path. The individual values remain exported if you need to wire the input manually or drive custom UI.
How it works
handleInputChangeFile — sends the selected file to {iri}/upload as multipart/form-data (not base64). Sets updating.value = true during the upload. On success the resource store updates automatically.
handleInputDeleteFile — shows a confirmation dialog, then sends PATCH {iri} with { [filename]: null }. The API deletes the stored file.
filenameInputModel — bind with v-model on CwaUiFormFile. Shows the current filename when a file exists, empty otherwise.
CwaUiFormFile props
bind supplies the wiring; add label (and optionally accept) per field:
<CwaUiFormFile
v-bind="bind" <!-- v-model, fileExists, disabled, change, delete -->
label="Image"
accept="image/*" <!-- optional MIME restriction -->
/>
Complete admin upload tab example
<!-- app/cwa/components/HeroSection/admin/ImageTab.vue -->
<template>
<CwaUiFormFile v-bind="bind" label="Upload Image" />
</template>
<script setup lang="ts">
import { useCwaResourceManagerTab, useCwaResourceUpload } from '#imports'
const { exposeMeta, iri } = useCwaResourceManagerTab({ name: 'Upload' })
const { bind } = useCwaResourceUpload(iri)
defineExpose(exposeMeta)
</script>
Multiple file fields
There's no multi-file wrapper — call useCwaResourceUpload once per field, passing each field's property name. Because you can't spread a nested field.bind (it won't unwrap — see the callout above), destructure and rename each bind:
<template>
<CwaUiFormFile v-bind="posterBind" label="Poster" accept="image/*" />
<CwaUiFormFile v-bind="thumbnailBind" label="Thumbnail" accept="image/*" />
</template>
<script setup lang="ts">
import { useCwaResourceManagerTab, useCwaResourceUpload } from '#imports'
const { exposeMeta, iri } = useCwaResourceManagerTab({ name: 'Media' })
const { bind: posterBind } = useCwaResourceUpload(iri, 'poster')
const { bind: thumbnailBind } = useCwaResourceUpload(iri, 'thumbnail')
defineExpose(exposeMeta)
</script>
Each call reads and writes its own mediaObjects[<property>] key, so two fields on the same resource never couple at the composable level. If fields do appear to share a file, it's a resource-data issue (a lost _metadata.mediaObjects key), not this composable.