Attempting to assign a File object as a property to a component but receiving an empty object in return. I'm curious about the security implications of this as well

I created a modal for previewing avatars with a generic design:

<avatar-update :img-file="avatarFile" :show="avatarModalShow" :img-url="url" @close="avatarModalShow = !avatarModalShow" :change-avatar="updateCrop" @destroyUrl="imgUrl = null"> </avatar-update>

When submitting an avatar, I pass several properties to the AvatarUpdate component from my root.

HTML

<div>
    <label for="avatar" class="cursor-pointer thumbnail-link bg-white p-1 rounded-lg" href="">
        <img class="thumbnail" src="{{asset('images/avatars/avatar-1.png')}}">
    </label>
    <input id="avatar" class="hidden" type="file" @change="onFileChange"/>
</div>

Root

onFileChange: function(e) {
    const file = e.target.files[0];
    this.url = URL.createObjectURL(file);
    this.updateCrop = !this.updateCrop;
    this.avatarModalShow = !this.avatarModalShow;
    this.avatarFile = file;
},

Although I can see the file object in the onFileChange function, the {{imgFile}} property in the AvatarUpdate component appears empty. Why is it not passing the file object properly?

Could there be security measures preventing the transmission of the file object between components? I'm trying to understand if this is secure and why the object is showing as empty in the AvatarUpdate component.

Edit

This is my AvatarUpload component:

<modal v-show="show" heading="Avatar Preview" @close="close">
  <div class="flex flex-col">

    <h4 class="text-blue-light mb-5">The avatar will be automatically cropped from the center.</h4>

      <div class="flex flex-col items-center">
          <img class="w-2/3" :src="imgUrl">
      </div>

      <p>{{imgFile}}</p>

      <button class="mt-4 h-10 self-end text-center bg-third-color hover:bg-secondary-color text-white font-bold py-2 px-4 rounded" v-on:click="submitAvatar()">Submit</button>
    </div>

<script>

    export default {
        props: ['show','imgUrl','changeAvatar','imgFile'],
        data() {
          return {
            image: null,
            message: null
          }
        },
        methods: {
            close: function(){
              this.$emit('close');
            },

            submitAvatar: function(){
              console.log(file);
              axios({
                  method: 'POST',
                  url: '/profile/avatar',
                  data: {},
              }).then(function (response) {


              this.message = "Your avatar has been submitted";   

              }.bind(this))
              .catch(function (error) {
                  console.log(error);
              });
            }
        }
    }
</script>

I am able to get the blob from

this.url = URL.createObjectURL(file);
in the onFileChange function. My goal is to send the entire file object to the AvatarUpdate component using the :img-file="avatarFile" prop.

This would allow me to access the data in a Laravel controller request:

submitAvatar: function(){
  //Change here!
  var data = new FormData()
    var file = this.imgFile;
    data.append('avatar', file);

  axios({
      method: 'POST',
      url: '/profile/avatar',
      data: data,
  }).then(function (response) {


  this.message = "Your avatar has been submitted";   

  }.bind(this))
  .catch(function (error) {
      console.log(error);
  });
}

Laravel UserController

UserController

public function avatar(Request $request)
{
    return $request->hasFile('avatar');
}

Answer №1

When working with your code, remember that this.avatarFile = file represents a File object (which inherits from Blob). This means it cannot be directly used in an image src tag. If you inspect the browser, you'll see that the value of img:src is [object File], which is not what you intended.

To achieve your goal, consider using JavaScript MDN: FileReader.readAsDataURL.

Another option is to utilize JavaScript MDN: URL.createObjectURL(), but be cautious with memory management. Refer to JavaScript MDN: URL.createObjectURL() Usage notes for more details.

PS: It is recommended to first convert the File object to either data-url or object-url before passing it to the child component. Directly passing the File object may lead to reactivity issues.

Here's a simple demo that utilizes FileReader:

 // Vue.js configuration
Vue.config.productionTip = false

// Vue component for image preview
Vue.component('img-preview', {
  template: `<div>
              <img :src="imageBlob" alt="test"/>
             </div>`,
  props: ['imageBlob']
})

// Vue instance
new Vue({
  el: '#app',
  data() {
    return {
      imageObj: null
    }
  },
  methods:{
    onFileChange: function(ev) {
      const selectFile = ev.target.files[0]
      let reader  = new FileReader()
      reader.readAsDataURL(selectFile)
      reader.addEventListener('load', () => {
        this.imageObj = reader.result
      }, false)  
    },
  }
})
// Include Vue.js library
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>

// HTML structure
<div id="app">
  <div>
      <label for="avatar">
          <img-preview :image-blob="imageObj"></img-preview>
      </label>
      <input id="avatar" class="hidden" type="file" @change="onFileChange($event)"/>
  </div>
</div>

Here's another example using createObjectURL:

 // Vue.js configuration
Vue.config.productionTip = false

// Vue component for image preview
Vue.component('img-preview', {
  template: `<div>
              <img :src="imageBlob" alt="test"/>
             </div>`,
  props: ['imageBlob']
})

// Vue instance
new Vue({
  el: '#app',
  data() {
    return {
      imageObj: null
    }
  },
  methods:{
    onFileChange: function(ev) {
      const selectFile = ev.target.files[0]
      this.imageObj = URL.createObjectURL(selectFile)
    },
  }
})
// Include Vue.js library
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>

// HTML structure
<div id="app">
  <div>
      <label for="avatar">
          <img-preview :image-blob="imageObj"></img-preview>
      </label>
      <input id="avatar" class="hidden" type="file" @change="onFileChange($event)"/>
  </div>
</div>

Lastly, here's a demo showing how to pass a File Object directly to a child component:

 // Vue.js configuration
Vue.config.productionTip = false

// Vue component for image preview
Vue.component('img-preview', {
  template: `<div>{{imageBlob}}
              <img :src="internalImageObj" alt="test"/>
             </div>`,
  props: ['imageBlob'],
  data() {
    return {
      internalImageObj: ''
    }
  },
  watch: {
    imageBlob: function (newVal) {
      let reader  = new FileReader()
      reader.readAsDataURL(newVal)
      reader.addEventListener('load', () => {
        this.internalImageObj = reader.result
      }, false)  
    }
  }
})

// Vue instance
new Vue({
  el: '#app',
  data() {
    return {
      selectedFile: null
    }
  },
  methods:{
    onFileChange: function(ev) {
      this.selectedFile = ev.target.files[0]
    },
  }
})
// Include Vue.js library
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>

// HTML structure
<div id="app">
  <div>
      <label for="avatar">
          <img-preview :image-blob="selectedFile"></img-preview>
      </label>
      <input id="avatar" class="hidden" type="file" @change="onFileChange($event)"/>
  </div>
</div>

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

Result received from Firebase authentication sign-in

I am working on two Vue projects integrated with Firebase. The first project was set up using webpack-simple, while the second one used just webpack. I have noticed that when I call signInWithEmailAndPassword in the simple project, it only returns: {"uid" ...

Exploring jQuery: Choosing all li elements starting from a specific index to the end

Is there a way to select the last item, li:eq('+(lastItemIndex)+')', and all subsequent items until the end? Check out this JSFiddle demo for an example of changing elements in li from 3 onwards. ...

How to Effortlessly Populate Cascading Dropdowns in ASP.Net MVC 5 using a Reusable

I am currently working on an ASP.Net MVC 5 application that has multiple edit pages. Each edit page consists of various input elements such as textboxes, checkboxes, and dropdowns. I want to implement a functionality where the values of one dropdown are ch ...

What is the best way to apply a single slot-element to multiple elements?

Considering a slot structure like: <span slot="?" slot-scope="data>...</span> What placeholder should be used in place of the question mark to make it versatile for any implementation? ...

Leveraging the power of SCSS within Blade templates, much like Vue components

Currently, I am working with a combination of Laravel 8 and Vue.js to build a website that uses both Blade and Vue components. I am looking for a way to include SCSS code specifically in one of my Blade templates, similar to how it is done in a Vue compo ...

Maintaining selected options in select lists while updating model data in Angular

New to Angular and exploring the Product object with Sku objects nested inside. An app allows users to fetch a product, resulting in the Product object being assigned to $scope.product: var app = angular.module('app', []); app.controller(&apos ...

What is the method of including a null option in a dropdown menu?

I have a basic dropdown menu with the placeholder text "none." I want users to be able to clear their selection without adding another option to the dropdown. Can anyone help me achieve this? Thank you! Below is my code snippet: Check out the live demo h ...

implement an angular directive to apply a CSS element

I am utilizing AngularJS and ng-repeat to populate a dynamic list of studies. This list has the capability to toggle into child elements of each item, creating an accordion-style toggle list that can go up to three levels deep for each list item. I am curr ...

What is the best way to incorporate a range of details into a div using only jQuery, all while avoiding the use of data-

I'm struggling to find a concise way to explain this, so please bear with me. The information I'm sharing here is all just for example purposes and may sound strange. I have been working on creating a character select page where clicking on a cha ...

Mix up table data cells using Javascript/jQuery

Can anyone provide me with some helpful tips? I'm really struggling with this. My Objective I aim to create a table with two columns ("name" and "rating"), consisting of 5 rows. 2 of these rows should have a random "rating" between 6-10 2 other ro ...

Learn how to toggle the display of a div using jQuery, just like the functionality on the popular website

Visit Mashable here Below is the script I am currently using: $(document).ready(function(){ $(".show_hide5").mouseover(function(){ $('.selected').removeClass('selected'); $(this).next().fadeIn("slow").addClass(&apo ...

How can you display a set of components in React using TypeScript?

I'm currently working on rendering an array of JSX Components. I've identified two possible methods to achieve this. Method one (current approach): Create the array as an object type that stores the component properties and then build the JSX co ...

Complete a bootstrap row and begin a new row after every nth div element

I have a grid layout in Bootstrap that I will be filling with blog post thumbnails. <section class="container"> <div class="row thumbs"> <div class="col-sm-3">content</div> <div class="col-sm-3">content</div> ...

Efficiently flattening an array in JavaScript using recursive functions without the need for loops

Currently I am studying recursion and attempting to flatten an array without using loops (only recursion). Initially, I tried the iterative approach which was successful, but I am facing challenges with the pure recursive version: function flattenRecurs ...

An issue occurred during the construction of an angular project: The Tuple type '[]' with a length of '0' does not contain any elements at index '0'

When I run the command ng build --prod, I encounter the following error: ERROR in src/app/inventory/inv-parts-main-table/dialog-component/dialog-component.component.html(5,16): Tuple type '[]' of length '0' has no element at index &apo ...

Encountering issues with fs.writeFile function in a freshly set up Vue project

After initializing a new Vue project with vue cli, I encountered an error when attempting to write files in the main.js file. Below is the code snippet that caused the issue: const fs = require('fs'); // Data to be written to the file. let dat ...

Guide to embedding a qr code within a pdf document

I've been working on creating a PDF file generation feature where users can download the PDF with a QR code embedded in it. While I've successfully implemented the PDF generation part, I'm facing an issue with adding the QR code to the file. ...

Achieving dynamic population of a second dropdown menu based on selection from the first dropdown menu using PHP

In my current project, I am faced with the task of populating three different menus. The first menu is generated using a MySQL query in PHP and displays TV shows like "Modern Family" or "Dexter". What I want to achieve is that once a TV show is selected fr ...

Ways to extract innerHTML content from a loaded element generated by using the .load() method

Check out the code snippet below: template.html : <div id="nav"> Welcome to Space </div> layout.html : <div id="content"> </div> $('#content').load("template.html #nav"); ale ...

Is there a way to configure a Mui textfield to only allow numeric input? It currently accepts numbers, the letter "e," and dashes

Take a look at my current code. <TextField error={values[1].error} fullWidth id="id" type="number" value={values[1].Id} placeholder='Enter ID' onChange={handleChange(1,'Id')} variant="outlined" inputProps={{min: 0,inputMode: &apos ...