Обработка события из входного файла в компоненте VueJS

У меня есть группа дочерних компонентов, в которой я хочу изменить изображение:

<template>
    <div>
        <div>
            <form enctype="multipart/form-data">
                <input type="file" name="file" id="file" @change="change_file" class="py-6 px-8 hidden">
                <label for="file" class="flex flex-col items-center text-main-color text-sm py-2 px-4">
                    <span class="text-sm mt-2" style="color: #7eaeb7;">Click to change image</span>
                    <img :src="'/img/'+data.img" alt="" class="w-48 h-48 mx-auto p-4" >
                </label>
            </form>
        </div>

        <div class="flex flex-col items-center">
            <span class="">{{data.definition}}</span>
            <span>{{data.description}}</span>

            <div @click="edit_statment">
                <span>Edit Statment</span>
            </div>
        </div>
    </div>
</template>
<script>
    export default {
        props: ['data'],

        methods: {
            change_image(event) {
                let formData = new FormData();
                formData.append('file', event.target.files[0]);

                axios.post('/change_image', formData, {
                    headers: {
                      'Content-Type': 'multipart/form-data'
                    }
                })
                    .then(response => {
                        this.$emit('changeStatmentImg', response.data.new_image);
                    })
            },

            edit_statment() {
                this.$emit('editStatment');
            }
        }
    }
</script>

Я слушаю события на родительском компоненте:

<template>
<div class="">
    <statment v-for="item in statments" :key="item.id" :data="item" @editStatment="initialize(item, 'statment')" @changeStatmentImg="changeImage($event, item, 'statment')"></pr_course_statment>
</div>
</template>
<script>
    export default {
        data() {
            return {
                statments: [
                    {
                        img: 'module1.svg',
                        definition: 'First statment',
                        description: 'First statment description',
                    },
                    {
                        img: 'module2.svg',
                        definition: 'Second statment',
                        description: 'Second statment description',
                    },
                    {
                        img: 'module3.svg',
                        definition: 'Third statment',
                        description: 'Third statment description',
                    },
                ],
            }
        },

        methods: {
            initialize(item, model_name) {
                console.log('item.id: ', item.id);  
            },
            changeImage(file_name, item, model_name) {
                console.log(file_name);
                console.log(item.id); 
                console.log(model_name);
            },
        }
    }
</script>

Когда я испускаю событие из компонента, все идет хорошо в случае общего редактирования текстового поля (метод инициализации)

Но когда дочерний компонент выставляет событие во входном файле @change, он каждый раз использует только первый элемент из списка Statments.

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">

<div id="app" class="flex justify-between">
      <div v-for="item in statments">
                <div class="flex flex-col items-center">
                    <div class="flex flex-col items-center border border-indigo-200 rounded-md">
                        <form class="flex justify-center my-4" enctype="multipart/form-data">

                            <input type="file" name="file" id="file" @change="changeImage(item)" class="py-6 px-8 hidden">
                            <label for="file" class="flex flex-col items-center text-main-color text-sm py-2 px-4">
                                <span class="text-sm mt-2" style="color: #7eaeb7;">Click to change image</span>
                                <img :src="item.img" alt="" class="w-48 h-48 mx-auto p-4" >

                            </label>
                        </form>

                    </div>

                    <div class="mt-4 mb-6" @click="changeStatment(item)" class="flex flex-col items-center">
                        <span>{{item.definition}}</span>
                        <span class="text-indigo-500 inline-flex items-center md:mb-2 lg:mb-0 main-color">Edit Statment
                            <svg class="w-4 h-4 ml-2" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
                                <path d="M5 12h14"></path>
                                <path d="M12 5l7 7-7 7"></path>
                            </svg>
                        </span>
                    </div>
                </div>  
            </div>
</div>

<script>
var app = new Vue({
  el: '#app',
        data() {
            return {
                statments: [
                    {
                        id: 1,
                        img: 'module1.svg',
                        definition: 'First statment',
                        description: 'First statment description',
                    },
                    {
                        id: 2,
                        img: 'module2.svg',
                        definition: 'Second statment',
                        description: 'Second statment description',
                    },
                    {
                        id: 3,
                        img: 'module3.svg',
                        definition: 'Third statment',
                        description: 'Third statment description',
                    },
                ],
            }
        },
        methods: {
            changeStatment(item) {
                console.log('item.id: ', item.id);  
            },
            changeImage(item) {
                console.log('item.id from form input: ', item.id);
            },
        }
})
</script>


person Андрей Измайлов    schedule 24.10.2020    source источник
comment
В чем проблема?   -  person Naren    schedule 24.10.2020
comment
проблема в том, что при попытке изменить изображение для компонента 3 оно меняется для компонента 1 каждый раз   -  person Андрей Измайлов    schedule 24.10.2020


Ответы (1)


Я почти уверен, что это из-за :key="item.id" в вашем родительском компоненте, то есть undefined.

:key-атрибут важен для внутреннего рендеринга и кэширования vue и ДОЛЖЕН быть глобально уникальным (а не только уникальным для ЭТОГО компонента).

Я всегда использую что-то вроде этого:

<div v-for="(item, idx) in items" :key="`Items_${_uid}_${idx}`" />

_uid будет увеличиваться для каждого визуализируемого vue-компонента, поэтому он никогда не может быть одинаковым.
idx требуется, чтобы различать элементы внутри этого vue-компонента (которые все находятся внутри одного и того же vue-компонента и, следовательно, имеют тот же _uid)

person Iyashi    schedule 24.10.2020
comment
Я старался. К сожалению, результат тот же. Фрагмент кода иллюстрирует суть проблемы - person Андрей Измайлов; 24.10.2020