Search code examples
imagevue.jsvue-component

image preview disappear while editing the other form value in VueJs


i am using VueJs for my frontend and i have form with text and file inputs. while uploading file i am showing image preview. image preview appears perfectly but as soon as i edit other form fields , the preview disappears. don't understand what is happening here. please help..

here i'm adding some codes,

this is the Whole code of fileupload.vue

<template>
  <div>
    <div class="row">
      <div class="col-lg-12">
        <div class="card">
          <form role="form" @submit.prevent="saveImage">
            <div class="card-body">
          <div class="row">
            <div class="form-group col-md-12">
              <label for="note">Description</label>
              <textarea
                id="desc"
                v-model="form.desc"
                type="text"
                class="form-control"
                :class="{ 'is-invalid': form.errors.has('desc') }"
                name="desc"
              ></textarea>
              <has-error :form="form" field="desc" />
            </div>
            <div class="form-group col-md-3 col-xl-3">
              <label for="input1">Input 1</label>
              <input
                id="input1"
                v-model="form.input1"
                type="number"
                class="form-control"
                :class="{ 'is-invalid': form.errors.has('input1') }"
                name="input1"
              />
              <has-error :form="form" field="input1" />
            </div>
            <div class="form-group col-md-3 col-xl-3">
              <label for="input2">Input 2</label>
              <input
                id="input2"
                v-model="form.input2"
                type="number"
                class="form-control"
                :class="{ 'is-invalid': form.errors.has('input2') }"
                name="input2"
              />
              <has-error :form="form" field="input2" />
            </div>
            <div class="form-group col-md-6 col-xl-4">
              <label for="image">Main Image</label>
              <div class="custom-file">
                <input
                  id="image"
                  type="file"
                  class="custom-file-input"
                  name="image"
                  :class="{ 'is-invalid': form.errors.has('image') }"
                  @change="onFileChange"
                />
                <label class="custom-file-label" for="image">{{$t('common.choose_file')}}</label>
              </div>
              <has-error :form="form" field="image" />
              <div class="bg-light mt-4 w-25">
                <img
                  v-if="url"
                  :src="url"
                  class="img-fluid"
                  :alt="$t('common.image_alt')"
                />
                </div>
            </div>

            <div class="form-group col-md-6 col-xl-4">
              <div class="custom-file">
                <input
                  multiple="true"
                  id="all_image"
                  ref="files"
                  type="file"
                  class="custom-file-input"
                  name="all_image"
                  :class="{ 'is-invalid': form.errors.has('all_image') }"
                  @change="onFileMultipleChange"
                />
                <label class="custom-file-label" for="all_image">{{
                  $t('common.choose_file')
                }}</label>
              </div>
              <has-error :form="form" field="all_image" />
              <div class="bg-light mt-4 w-50 row" style="width: 100% !important;">
                <div class="file-listing col-md-6"  v-for="(file, key) in files" :key="key">
                  <img :src="file.image" v-bind:ref="'image'+parseInt( key )" style="height: 150px;width: 150px;"/>
                </div>
              </div>
            </div>

        </div>
        </div>
        <div class="card-footer">
          <v-button :loading="form.busy" class="btn btn-primary">
            <i class="fas fa-save" /> {{ $t('common.save') }}
          </v-button>
          <button
            type="reset"
            class="btn btn-secondary float-right"
            @click="resetform()">
            <i class="fas fa-power-off" /> {{ $t('common.reset') }}
          </button>
        </div>
      </form>
    </div>
      </div>
    </div>
  </div>
</template>

<script>
 import { mapGetters } from 'vuex'
 import Form from 'vform'
 export default {
  middleware: ['auth', 'check-permissions'],
data: () => ({
form: new Form({
  image: '',
  all_image:[],
  desc:'',
  input1:'',
  input2:''

}),
url: null,
files:[],
  }),
 methods: {
onFileChange(e) {
  const file = e.target.files[0]
  const reader = new FileReader()
  if (
    file.size < 2111775 &&
    (file.type === 'image/jpeg' ||
      file.type === 'image/png' ||
      file.type === 'image/gif')
  ) {
    reader.onloadend = (file) => {
      this.form.image = reader.result
    }
    reader.readAsDataURL(file)
    this.url = URL.createObjectURL(file)
  } else {
    Swal.fire(
      this.$t('common.error'),
      this.$t('common.image_error'),
      'error'
    )
  }
},

getImagePreviews(){
  for( let i = 0; i < this.files.length; i++ ){
    if ( /\.(jpeg|jpg|png|gif|mov|mp4|mkv)$/i.test( this.files[i].name ) ) {
      let reader = new FileReader();
      let iname = /\.(jpeg|jpg|png|gif)$/i.test( this.files[i].name );
      reader.addEventListener("load", function(){
        if(iname == true){
          this.$refs['image'+parseInt( i )][0].src = reader.result;
        }
    this.form.other_image[i] = reader.result
  }.bind(this), false);
  reader.readAsDataURL( this.files[i] );
    }
  }
},
// vue file upload
onFileMultipleChange(e) {
let uploadedFiles = e.target.files;
  for( var i = 0; i < uploadedFiles.length; i++ ){
     this.files.push( uploadedFiles[i] );
}
this.getImagePreviews();
},

   async resetform() {
    window.location.reload();
  },
  async saveImage() {
  //form data post code
  },
   },
  }
 </script>
<style lang="scss" scoped>
</style>

Solution

  • When I put your component into a playground, everything seems to work. Please confirm that the error does not occur there.

    You can adjust the playground until it reproduces the error and then add it to your question. Until we can actually see the error, there is nothing we can do for you.


    As you requested, here is how you could build the image preview without using ref. Instead of putting the data URLs into the img tags through $refs, the data URLs are written to a regular data array and then used directly in the v-for. I have also moved it into its own component, so it does not clutter the other already huge component:

    const ImagePreviewsComponent = {
      props: ['images'],
      template: `
      <div class="bg-light mt-4 w-50 row">
        <div class="file-listing col-6" v-for="(dataUrl, key) in imagePreviews" :key="key">
          <img :src="dataUrl" class="w-100"/>
        </div>
      </div>
      `,
      data() {
        return {
          imagePreviews: [],
        };
      },
      watch: {
        images: {
          async handler(){
            this.imagePreviews = []
            const promises = [...this.images].map(image => this.loadImagePreview(image))
            this.imagePreviews = await Promise.all(promises)
          },
          immediate: true,
        }
      },
      methods: {
        async loadImagePreview(image){
          const reader = new FileReader()
          const promise = new Promise(resolve => reader.onload = resolve)
          reader.readAsDataURL(image)
          await promise
          return reader.result
        }
      }
    }
    
    new Vue({
      el: '#app',
      components: {'image-previews': ImagePreviewsComponent},
      data() {
        return {
          images: [],
        };
      },
    })
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css">
    <div id="app" class="m-2">
    
      <div class="form-group col-md-6 col-xl-4">
        <div class="custom-file">
          <input
            multiple="true"
            type="file"
            @change="e => images = e.target.files ?? e.dataTransfer.files"
          />
        </div>
        
        <image-previews :images="images"></image-previews>
    
      </div>
    
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.7.10/vue.min.js"></script>

    Here it is in the playground from before.