Creating an upload file input form within a VueJS Application

One of the challenges I'm facing involves creating a VueJS component with an input field for file type. Below is the code for my component:

<template>
    <div class="flex-col justify-start w-full">
        <div class="mt-2 block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2">{{ label }}</div>
        <input
            class="block appearance-none w-full bg-gray-200 border border-gray-200 text-gray-700 py-3 px-4 pr-8 rounded leading-tight focus:outline-none focus:bg-white focus:border-gray-500"
            :class="errorDisplay ? 'border-red-500 focus:bg-white focus:border-red-500': ''"
            type="file"
            :value="value" @input="emitEvent($event)"
            ref="input_file"
        >
        <span v-if="hint" class="text-xs text-gray-400 font-medium">{{ hint }}</span>
        <span v-if="errorDisplay" class="text-xs text-pink-600 font-medium">{{ errorDisplay }}</span>
    </div>
</template>

<script>
    export default {
        name: "InputFile",
        props: {
            label: String,
            hint: {
                type: String,
                default: () => ''
            },
            error: {
                type: Array,
                default: () => []
            },
            placeholder: String,
            value: Object,
        },
        methods: {
            emitEvent(event) {
                var reader = new FileReader();
                reader.readAsDataURL(event.target.files[0]);
                reader.onload = () => {
                    const docs = {
                        name: event.target.files[0].name,
                        size: event.target.files[0].size,
                        lastModifiedDate: event.target.files[0].lastModifiedDate,
                        base64: reader.result
                    };
                    console.log(docs);
                    this.$emit('input', docs)
                };
            }
        },
        computed: {
            errorDisplay() {
                if(this.error.length)
                    return this.error.join(', ');
                else
                    return '';
            }
        }
    }
</script>

When calling my component, I encountered the following issue:

<template>
    <div class="flex items-center justify-start">
        <div class="w-1/2 m-2 rounded-lg shadow-lg border b-gray-400 rounded flex flex-col justify-start items-start p-6 bg-white">
            <div class="border-b -m-2 mb-3 px-6 py-2 flex-none w-full justify-start text-gray-700 font-semibold"> Base Controls </div>
            <input-file
                    label="Upload file"
                    v-model="upload_file"
                    :error="errors['upload_file']"
            >
            </input-file>
            <div class="mt-4 text-center">
                <button @click="submit()" class="inline-block px-4 py-2 rounded-lg shadow-md bg-teal-500 hover:bg-teal-400 focus:outline-none focus:shadow-outline text-white text-sm tracking-wider font-semibold">Submit</button>
            </div>
        </div>
    </div>
</template>

<script>
    import InputFile from "../Elements/Forms/Inputs/File";
    export default {
        name: "Forms",
        components: {
            InputFile,
        },
        data() {
            return {
                upload_file: '',
                errors: {},
            }
        },
        methods: {
            submit() {
                //Submit code...
            }
        }
    }
</script>

An error message I'm encountering reads as follows:

Error in nextTick: "InvalidStateError: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string."

Although the event is being emitted and upload_file is successfully assigned the desired value, I'm still facing this issue. I attempted to change upload_file to an object, but this resulted in an error and the component not being displayed. How can I go about resolving this concern?

Answer №1

It seems that the issue arises from attempting to set the 'value' property of the input element (by binding it to prop.value)

When dealing with file-type input elements, it is not possible to directly modify the value property as you would with other types, as discussed here.

To resolve this in your custom component, you can remove the binding in the template, :value="value", and in the script, either:

  • remove the prop value: Object or,
  • if the value prop is needed for v-model compatibility, assign it to File. For example: value: File

Note: This approach will work, but you may receive a Vue warning for an invalid prop if the component is called without a provided file.

For instance...

<template>
    <div class="flex-col justify-start w-full">
        <div class="mt-2 block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2">{{ label }}</div>
        <input
            class="block appearance-none w-full bg-gray-200 border border-gray-200 text-gray-700 py-3 px-4 pr-8 rounded leading-tight focus:outline-none focus:bg-white focus:border-gray-500"
            :class="errorDisplay ? 'border-red-500 focus:bg-white focus:border-red-500': ''"
            type="file"
            @input="emitEvent($event)"
            ref="input_file"
        >
        <span v-if="hint" class="text-xs text-gray-400 font-medium">{{ hint }}</span>
        <span v-if="errorDisplay" class="text-xs text-pink-600 font-medium">{{ errorDisplay }}</span>
    </div>
</template>

<script>
    export default {
        name: "InputFile",
        props: {
            label: String,
            hint: {
                type: String,
                default: () => ''
            },
            error: {
                type: Array,
                default: () => []
            },
            placeholder: String,
            value: File,
        },
        methods: {
            emitEvent(event) {
                var reader = new FileReader();
                reader.readAsDataURL(event.target.files[0]);
                reader.onload = () => {
                    const docs = {
                        name: event.target.files[0].name,
                        size: event.target.files[0].size,
                        lastModifiedDate: event.target.files[0].lastModifiedDate,
                        base64: reader.result
                    };
                    console.log(docs);
                    this.$emit('input', docs)
                };
            }
        },
        computed: {
            errorDisplay() {
                if(this.error.length)
                    return this.error.join(', ');
                else
                    return '';
            }
        }
    }
</script>

This adjustment should suffice.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Stay tuned for Quasar's input validation event notifications

Within my Vue3 Quasar application, I have implemented a component structure similar to the following: <template> <div class="q-pa-md"> <div id="myCustomLabel">{{ props.label }}</div> <q-input r ...

Mapping the correct syntax to apply font styles from an object map to the map() function

React, JS, and Material UI Hey there, I have a question about my syntax within the style property of my map function. Am I missing something? Fonts.jsx export const fonts = [ { fontName: 'Abril', key: 'abril', fontFamily ...

Is it possible to save round items within a session?

While utilizing Blocktrail's API for bitcoin wallet management, I encountered an issue with circular references within the returned wallet object. The goal is to store the decrypted wallet in the user's session to avoid password re-entry. Howeve ...

Update the sum upon deleting a feature in an HTML and JavaScript form

Once a row or field is added, this function will display a delete link. When clicked, it will remove the row or field: var ct = 1; function addNewRow(){ ct++; var divElement = document.createElement('div'); divElement.id = ct; // Code to cr ...

Is there a way to exchange the chosen options between two bootstrap-vue multiple select boxes?

Is there a way to switch the selected options between two bootstrap-vue multiple select boxes? I am trying to create a UI similar to the image shown below but I am struggling with writing the method. This is how I have written the template: https://i.sst ...

How can I retrieve the top-level key based on a value within a JSON object nested in JavaScript?

If I have the value Africola for key name in the following nested JSON structure, how can I retrieve its corresponding upper-level key 'barID1' using JavaScript? { "barID1": { "address": "4 East Terrace, Sydney NSW 2000", "appStoreURL" ...

Combining backend webpack with Vue-CLI 3 for seamless integration

As I work on a full-stack application with Vue-CLI 3, the backend side is built in TypeScript which requires compilation. Currently, I am running the backend app directly using ts-node, but I am looking for a cleaner solution to bundle the backend code int ...

The axios.get.mockResolvedValue() statement in the testfile.test.ts file for Vue/Vitest is causing an issue that needs to be

Currently deep in the process of integrating vitest into my Vue app and running successful unit tests. However, encountering an error message that reads: 'The property "mockResolvedValue" is for type "<T = any, R = AxiosResponse<T ...

Which Angular Lifecycle event should I utilize to trigger a specific action when either two buttons are pressed or when one of the buttons undergoes a change?

In this scenario, we have a total of 6 buttons split into two groups of 3: one on the top and the other on the bottom. let valuesum; let value1; let value2; let ButtonGroup1clicked= false; let buttonGroup2Clicked= false; function click1 (value){ va ...

Safari's Web Audio API suffering from subpar performance and various shortcomings

For my University project, I am developing an HTML and JavaScript-based mp3 player using the Web Audio API. You can check out the progress of this project by visiting this link: While everything is running smoothly on Firefox and Chrome, Safari is posing ...

Steering clear of Unique error E11000 through efficient handling with Promise.all

In my development work, I have been utilizing a mongoose plugin for the common task of performing a findOrCreate operation. However, I recently discovered that running multiple asynchronous findOrCreate operations can easily result in an E11000 duplicate k ...

Prioritize returning AJAX data over iterating through the parent loop

Hey everyone, I'm having trouble wrapping my head around this issue even after researching various scenarios online. Here's what's going on: I have an array of strings named recipientsList. What I want to do is make an AJAX call for each s ...

The React Query devtools are failing to display

I am currently working on a project using React Query, but for some reason, the DevTools icon is not appearing on my screen. I have checked the console for errors, but there are none. I am following a tutorial on YouTube to help me with this. Here is a sn ...

Inability to load a MySQL table using Ajax on the same webpage

I'm facing an issue where I want to load a table from mySql on the same page without getting redirected to another page. The user selects a date range, and upon pressing submit, it should appear in the designated div id tag. However, the functionality ...

How to use jQuery to extract a particular text from an anchor tag

If I want to choose a specific anchor text and make changes to it, I can do so by targeting anchors with a certain href attribute. For example, on a page with multiple unordered lists, each containing different links: <ul> <li><a href="v ...

Vue JS: When Computed Properties Fail to Trigger

I am currently using Vue.js and have implemented a computed property stored within my vuex. The issue I am facing is that when I add an item to the array, the watcher does not trigger. However, upon logging the item, it is indeed present. computed:{ ...

Strings that automatically adjust to fit the content from a JSON object

I have a JSON object with a long string that I want to display. The string looks like this: "String":"<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever si ...

Displaying an IP camera feed on a three.js canvas

I've been presented with a seemingly straightforward task in JavaScript that I'm not very familiar with. The challenge is to take an IP camera stream and display it on a three.js canvas. To start, I came across an example that uses a webcam inst ...

Determine the percentage of payments, similar to Upwork, by leveraging the power

Currently, I am working on developing a percentage calculation similar to what is seen on the Upwork website. The calculation appears to be accurate when derived from the base amount at a rate of 10%. For instance, if we take a base amount of 89.00, the ...

What is the reason for findUser.username returning as unidentified?

I am encountering an issue where the findUser.username is being printed in my console.log, but the error persists. I would appreciate some assistance with this problem. Thank you. const auth_user = [ { username: "amylussie", password: ...