Home > Enterprise >  Renaming/removing uploaded files - Vue.js
Renaming/removing uploaded files - Vue.js

Time:03-08

The requirement is to rename the files before uploading it to the system. So I tired using v-model but failed. So I came up with the following which is working great just that I cannot retain my input text value. The preventdefault isn't working.

Here I uploaded 3 images and rename them as First, Second and Third.

<div>
    <input type="file" @change="addFile" multiple>Choose file
</div>

<div v-if="selectedFiles && selectedFiles.length>0">
       <div v-for="(item, index) in selectedFiles">
            <span>{{ item.name }}</span>
            <input type="text" v-model="" placeholder="Rename File" @change="renameFile(item,event)"/>
            <button @click="removeFile(event,item,index)"></button>                                    
        </div>
 </div>   
 <button @click="uploadDrawings">Upload Files</button>                                        
data: {
      selectedFiles: [],
      renameFiles: [],
      finalFiles: [], 
},
methods: {

    addFile(event) {
        _.each(Array.from(event.target.files), function (file) {

            var existed = _.find(app.selectedFiles, function (item) {
                return item.name == file.name;
            });

            if (!existed) {
                app.selectedFiles.push(file);
            }
        });
    },
    renameFile(item, event) {
        if (app.renameFiles.filter(x => x.oldName == item.name).length > 0) {
            var objIndex = app.renameFiles.findIndex(x => x.oldName == item.name);
            app.renameFiles[objIndex].newName = event.target.value;
        }
        else {
            app.renameFiles.push({
                oldName: item.name,
                newName: event.target.value
            })
        }
    },
    removeFile(e,item, index) {
        e.preventDefault(); // while removing an item, the text value changes.
        app.selectedFiles.splice(index, 1);
        var objIndex = app.renameFiles.findIndex(x => x.oldName == item.name);
        app.renameFiles.splice(objIndex, 1);
    },

    uploadDrawings() {
       app.isLoading = true;
       if (app.selectedFiles.length == 0) {
         return;
     }

     _.each(app.selectedFiles, function (item) {
          var blob = item.slice(0, item.size, 'image/jpg');
          var name = app.renameFiles.filter(x => x.oldName == item.name);
          app.finalFiles.push(new File([blob], name[0].newName   '.jpeg', { type: 'image/jpg' }));
});
}

So, On removing item from top to bottom the text box does not retain its value. First was assigned for 091646.

However, the finalFiles has the correct naming files. Its just the textbox which has a problem.

Please help me to retain the textbox values. Thanks in advance.

CodePudding user response:

Missing keys

The items in the v-for are not explicitly keyed in your markup, so Vue automatically keys them by their index. As a rendering optimization, Vue reuses existing elements identified by their keys. This can cause an issue when an item is added to or removed from the middle of the list, where surrounding items take on new keys equal to their new indices.

Solution

Apply the key attribute to each v-for item using a unique ID (not equal to index). In this case, the index combined with the filename would suffice:

<div v-for="(item, index) in selectedFiles" :key="index   item.name">

Missing v-models

When the template is re-rendered (occurs when removing an item from the middle of the list), the textboxes lose their values. This is because they have no model bound to them.

Solution

Update renameFiles[] to store only strings (instead of objects), where each string corresponds to a file in selectedFiles[] by the same index. That would obviate the file lookup code throughout your methods, so the code would be simplified to this:

export default {
  ⋮
  methods: {
    addFile(event) {
      this.selectedFiles = Array.from(event.target.files)
    },
    removeFile(e, item, index) {
      this.selectedFiles.splice(index, 1)
      this.renameFiles.splice(index, 1)
    },
    ⋮
  },
}

That change also enables using renameFiles[] as v-model on the <input>s for the rename strings:

<input type="text" v-model="renameFiles[index]" placeholder="Rename File" />

demo

  • Related