Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/runtime/components/FileUpload.vue
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ export interface FileUploadProps<M extends boolean = false> {
* @defaultValue true
*/
interactive?: boolean
/**
* Skip preview and directly emit update event.
* @defaultValue false
*/
directUpdate?: boolean
required?: boolean
disabled?: boolean
/**
Expand Down Expand Up @@ -144,6 +149,7 @@ const props = withDefaults(defineProps<FileUploadProps<M>>(), {
dropzone: true,
interactive: true,
fileDelete: true,
directUpdate: false,
layout: 'grid',
position: 'outside'
})
Expand Down Expand Up @@ -213,6 +219,14 @@ function formatFileSize(bytes: number): string {
}

function onUpdate(files: File[], reset = false) {
// @ts-expect-error - 'target' does not exist in type 'EventInit'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When directUpdate is enabled, the component emits the change event with raw files but never updates modelValue, causing files to not display in the UI and creating a disconnect between the event payload and component state.

View Details
πŸ“ Patch Details
diff --git a/src/runtime/components/FileUpload.vue b/src/runtime/components/FileUpload.vue
index e481598a..e56ff625 100644
--- a/src/runtime/components/FileUpload.vue
+++ b/src/runtime/components/FileUpload.vue
@@ -219,14 +219,7 @@ function formatFileSize(bytes: number): string {
 }
 
 function onUpdate(files: File[], reset = false) {
-  // @ts-expect-error - 'target' does not exist in type 'EventInit'
-  let event = new Event('change', { target: { value: files } })
-
-  if (props.directUpdate) {
-    emits('change', event)
-    return
-  }
-
+  // Update modelValue first to ensure files display in UI
   if (props.multiple) {
     if (reset) {
       modelValue.value = files as (M extends true ? File[] : File) | null
@@ -238,6 +231,14 @@ function onUpdate(files: File[], reset = false) {
     modelValue.value = files?.[0] as (M extends true ? File[] : File) | null
   }
 
+  // @ts-expect-error - 'target' does not exist in type 'EventInit'
+  let event = new Event('change', { target: { value: files } })
+
+  if (props.directUpdate) {
+    emits('change', event)
+    return
+  }
+
   // @ts-expect-error - 'target' does not exist in type 'EventInit'
   event = new Event('change', { target: { value: modelValue.value } })
   emits('change', event)

Analysis

FileUpload onUpdate() bypasses modelValue update when directUpdate=true

What fails: FileUpload component's onUpdate() function (lines 221-228) returns early when directUpdate is true without updating modelValue, causing uploaded files not to display in the UI

How to reproduce:

// Mount FileUpload with directUpdate=true
const wrapper = mount(FileUpload, { props: { directUpdate: true } })
const input = wrapper.find('input')
await setFilesOnInput(input, [new File(['test'], 'test.jpg')])
// modelValue remains null, template shows no files

Result: Template condition v-if="modelValue && (Array.isArray(modelValue) ? modelValue.length : true)" evaluates to false, files don't render in UI despite successful upload and event emission

Expected: Files should display in dropzone UI regardless of directUpdate mode - modelValue should be updated before the early return in directUpdate path

Fix: Move modelValue update logic before directUpdate check so template can render files in both modes while preserving different event payload behaviors

let event = new Event('change', { target: { value: files } })

if (props.directUpdate) {
emits('change', event)
return
}

if (props.multiple) {
if (reset) {
modelValue.value = files as (M extends true ? File[] : File) | null
Expand All @@ -225,7 +239,7 @@ function onUpdate(files: File[], reset = false) {
}

// @ts-expect-error - 'target' does not exist in type 'EventInit'
const event = new Event('change', { target: { value: modelValue.value } })
event = new Event('change', { target: { value: modelValue.value } })
emits('change', event)
emitFormChange()
emitFormInput()
Expand Down
Loading