Home > database >  Cannot get vue binding right, resorting to $emit
Cannot get vue binding right, resorting to $emit

Time:09-03

How do I get this example to work with two way binding?

I am creating a file upload component in vue like:

window.Vue.component('base64fileupload', {
    template:
        '<input type="file" @change="on_file_changed()"/>',

    methods:
    {
        on_file_changed()
        {
            var self = this;
            var reader = new FileReader();

            var file = this.$el.files[0];
            reader.readAsDataURL(file);

            reader.onloadend = function (evt)
            {
                if (evt.target.readyState == FileReader.DONE)
                {
                    self.$emit('filename', file.name)
                    self.$emit('filedata', evt.target.result)
                }
            }
        }
    }
});

and use the following html:

<base64fileupload @filename="file_edit.filename = $event"
  @filedata="file_edit.filedata = $event"></base64fileupload>

However, that is a bit manual, I expect to be able to bind data instead of using event handlrers. However, $emit is the only thing I have got working, despite a lot of testing and googling.

I have tried data() and props and self.$forceUpdate() in numerous ways.

It is not very easy to well why this is not googlable right away, but what I always find is typescript examples and usage of inside .vue-files, none of which I use.

CodePudding user response:

It seems that you've a reactivity issue, to solve it use the this.$set method to mutate the inner fields :

<base64fileupload @filename="$set(file_edit,'filename', $event)"
  @filedata="$set(file_edit, 'filedata', $event)"></base64fileupload>

If you want two way binding, you've to define prop named value and emit an event called input (this valid for vue 2) :

base64fileupload :

window.Vue.component('base64fileupload', {
    template:
        '<input type="file" :value="value" @change="on_file_changed"/>',
    props:['value'],
    methods:
    {
        on_file_changed()
        {
            var self = this;
            var reader = new FileReader();

            var file = this.$el.files[0];
            reader.readAsDataURL(file);

            reader.onloadend = function (evt)
            {
                if (evt.target.readyState == FileReader.DONE)
                {
                    self.$emit('input', {name:file.name, filedat:evt.target.result})
                  
                }
            }
        }
    }
});

Use it like :

<base64fileupload v-model="file_edit"></base64fileupload>

CodePudding user response:

You could create your own custom FileUpload component and then it could be simpler to just v-model it.

FileUpload.vue

<template>
  <div>
    <input type="file" :value="modelValue" @input="onFileInputChange" />
  </div>
</template>

<script>
export default {
  name: 'FileUpload',
  data() {
    return {
      modelValue: null,
    };
  },
  methods: {
    onFileInputChange(event) {
      let reader = new FileReader();

      console.log(event.target.files);
      let file = event.target.files[0];
      reader.readAsDataURL(file);

      reader.onloadend = (evt) => {
        if (evt.target.readyState == FileReader.DONE) {
          this.$emit('update:modelValue', {
            name: file.name,
            data: evt.target.result,
          });
        }
      };
    },
  },
};
</script>

Use it like.

<FileUpload v-model="file" />
  • Related