Zeus
Zeus

Reputation: 331

Vuetify V-File-Input Multiple Files

Pretty straightforward. Trying to get multiple files to display but the only one will since there is always only one in the array. When adding another file, the one uploaded before it gets overwritten. this.files is always just one file and and wont add on instead. How can I add on to files, instead of always overwriting? Any help or direction would be greatly appreciated.

<v-file-input
  v-model="files"
  small-chips
  show-size
  multiple
  clearable
>
  <template v-slot:selection="{ text, index, file }">
    <v-chip
      small
      text-color="white"
      color="#295671"
      close
      @click:close="remove(index)"
    >
      {{ text }}
    </v-chip>
  </template>
</v-file-input>
<script>
export default {
  data: () => ({
    files: []
  }),
  methods: {
    remove (index) {
      this.files.splice(index, 1)
    }
  }
}
</script>

Working Example: https://codepen.io/jhernandez_dev/pen/YzzRxMq?&editable=true&editors=101#anon-signup

Upvotes: 4

Views: 15612

Answers (4)

My problem was to add multiple files from multiple locations on the drive.

I succeed to achieve that by adding a simple watcher :

new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  data: () => ({
    currFiles: [],
    previousFiles: [],
    files: []
  }),
  watch: {
    files(val) {
        this.previousFiles = val
    }  
  },
  methods: {
    remove (index) {
      this.files.splice(index, 1)
    },
    fileAdded () {
      console.log(this.files)
      if (this.previousFiles.length > 0) {
        this.files.push(...this.previousFiles)
      }
    }
  }  
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
    <v-app>
        <div class="container">
            <v-file-input v-model="files" 
                small-chips 
                :show-size="1000"
                counter
                outlined
                placeholder="Select your files"
                multiple 
                clearable 
                label="Add files"
                @change="fileAdded">
                <template v-slot:selection="{ text, index, file }">
                    <v-chip small text-color="white" color="#295671" close @click:close="remove(index)">
                        {{ text }}
                    </v-chip>
                </template>
            </v-file-input>
        </div>
    </v-app>
</div>

Demo CodePly

Upvotes: 0

tbhaxor
tbhaxor

Reputation: 1943

You can do one thing

  1. Create a hidden input tag and add a reference to it
  2. Create a v-select component with prepend icon and append outer icon as a part of actions

Template

<input type="file" hidden multiple ref="files" @change="listFiles">
<v-select
  v-model="files"
  :items="files"
  chips
  readonly
  prepend-icon="attach_file"
  multiple
  @click="$refs.files.click()"
  @click:prepend="$refs.files.click()"
  @click:append-outer="uploadHere"
  label="Files"
  append-icon
  append-outer-icon="cloud_upload"
></v-select>

Script

export default {
  data() {
    return {
      files: []
    };
  },
  methods: {
    listFiles() {
      this.files = [];
      for (let i = 0; i < this.$refs.files.files.length; i++)
        this.files.push(this.$refs.files.files[i].name);
    },
    uploadHere()
    {
      console.log("Uploading");
      let formData = new FormData();

      // iteratate this.$refs.files.files

      // add data to formData

      // Post the form data with 'Content-Type': 'multipart/form-data' via fetch or Axios
    
      console.log("Uploaded");
      this.files = []
    }
  },
  created() {}
};

PS: This is an approach. If you find any difficulty in understanding, comment below

Upvotes: 2

Carol Skelly
Carol Skelly

Reputation: 362360

I solved this problem by merging 2 file arrays. One for the current selected files, and another for all files...

new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  data: () => ({
    currFiles: [],
    files: []
  }),
  methods: {
    remove (index) {
      this.files.splice(index, 1)
    },
    inputChanged () {
      console.log(this.files)
      this.files = [
          ...this.currFiles,
          ...this.files
      ]
    }
  }  
})

Demo

Upvotes: 4

Ratnakar Pedagani
Ratnakar Pedagani

Reputation: 1

You can try doing something like this.

<template>
  <v-file-input
    id="uploadedfiles"
    v-model="files"
    show-size
    counter
    multiple
    clearable
    label="File input"
    name="uploadedfiles"
    :rules="[filesizeLimit]"
    @change="inputChanged"
  >
    <template #selection="{ index, text }">
      <v-chip small label close color="primary" @click:close="deleteChip(index, text)">{{ text }}</v-chip>
    </template>
  </v-file-input>
</template>

<script>
export default {
  methods: {
    deleteChip(index, text) {
      // Prompt here with text if required
      this.previousFiles.splice(index, 1)
      this.files = this.previousFiles
    },
    inputChanged() {
      this.files = []
      const uploadedFiles = this.$refs.form.$el.querySelector('#uploadedfiles').files
      for (let i = 0; i < uploadedFiles.length; i++) {
        if (
          this.previousFiles !== undefined &&
          this.previousFiles !== null &&
          this.previousFiles.length <= 0
        ) {
          this.previousFiles.push(uploadedFiles[i])
        } else {
          const index = this.previousFiles.findIndex((x) => x.name === uploadedFiles[i].name)
          if (index >= 0) {
            this.previousFiles.splice(index, 1)
          }
          this.previousFiles.push(uploadedFiles[i])
        }
      }
      this.files = this.previousFiles
    },
  },
}
</script>

Upvotes: 0

Related Questions