Почему модель Vue не обновляется при input.val(value)?

Вопрос или проблема

У меня есть редактируемая таблица:

<div id="myTable">
   <b-table class="overflow-auto" :items="filtered" :fields="visibleFields" :filter="filter">
        <template v-for="field in editableFields" v-slot:[`cell(${field.key})`]="{ item }">
            <b-input v-model="item[field.key]" placeholder="--" @@change="update" />
        </template>
    </b-table>
</div>

Скрипт Vue(cdn)

    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
    <script src="https://unpkg.com/[email protected]/dist/bootstrap-vue.min.js"></script>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

    <script>

        new Vue({
            el: "#myTable",
            computed: {
                editableFields() {
                    return this.fields.filter(field => field.editable);
                },
                visibleFields() {
                    return this.fields.filter(field => field.visible)
                }
            },
            data: {
                filter: '',
                filtered: JSON.parse(`@Html.Raw(Model.SportBudgetsJson)`),
                fields: [
                    { key: 'Id', label: 'Id', visible: false },
                    { key: 'Field1', label: 'Field1', editable: true, visible: true, class: "percentageFormat pValue", thStyle: { width: "5%" } },
                    { key: 'Field2', label: 'Field2', editable: true, visible: true, class: "moneyFormat mValue", thStyle: { width: "10%" } },
                ]
            }
        });
...

И js функции для форматирования значений в таблице:

 function formatValue(value, event, isPercent) {
     ... // возвращает значение в формате ###.##% или $###,###.##
     return formattedValue;
 }

 function copyValue(element, isPercent) { // обновление значения в % если значение $ изменилось и наоборот
     var selector = isPercent ? '.mValue' : '.pValue';
     var input = $(element).parent('td').parent('tr').children(selector).first().children('input');
     var value = element.value.replace(/[^\d]+/g, '');
     value = converValue([value.slice(0, -2), '.', value.slice(-2)].join(''), isPercent);
     $(input).val(formatValue(value, 'input', !isPercent));
 }

 function converValue(value, isPercent) {
     value = parseFloat(value);
     var totalBudget = parseFloat(`@Model.TotalBudget`);
     if (isPercent) {
         return parseFloat(totalBudget * (value / 100)).toFixed(2);
     }
     else {
         return parseFloat(value / totalBudget * 100).toFixed(2);
     }
 }

 $(document).ready(function () { // применение событий форматирования
     $('.moneyFormat input').each(function (index, el) {
         $(el).val(formatValue(el, "input", false));
         $(el).on("input change", function (event) {
             $(this).val(formatValue($(this).val(), event, false));
             if ($(el).parent('td').first().hasClass('mValue'))
                 copyValue(this, false);
         });
     });

     $('.percentageFormat input').each(function (index, el) {
         $(el).val(formatValue(this, "input", true));
         $(el).on("input change", function (event) {
             $(this).val(formatValue($(this).val(), event, true));
             if ($(el).parent('td').first().hasClass('pValue')) {
                 copyValue(this, true);
             }
         });
     });
 });

Когда я использую $(input).val(formatValue(value, ‘input’, !isPercent)) значение в таблице обновляется, но v-model нет. Но когда я ввожу значение в поле вручную – v-model обновляется.

Я хочу реализовать обновление значений в реальном времени:
Общая сумма $100.00
Field1: изменяется с 12.00% на 13.00%, так что Field2 автоматически изменяется с $12.00 на $13.00.

Общая сумма $100.00
Field2: изменяется с $50.00 на $45.00, так что Field1 автоматически изменяется с 50.00% на 45.00%.

Значения должны вычисляться и вставляться в их поля.

Как мне обновить v-model для значений инпутов?

Манипуляции с DOM с помощью JQuery не обновляют автоматически реактивное связывание данных Vue, так как реактивная система Vue зависит от своих собственных методов для обнаружения изменений данных. Когда вы напрямую манипулируете DOM с помощью JQuery, Vue не знает об этих изменениях.

function copyValue(element, isPercent) { 
    var selector = isPercent ? '.mValue' : '.pValue';
    var row = $(element).closest('tr'); 
    var input = row.find(selector).first().children('input');
    var value = element.value.replace(/[^\d]+/g, '');
    value = converValue([value.slice(0, -2), '.', value.slice(-2)].join(''), isPercent);

    var index = row.index(); 
    if (isPercent) {
        this.filtered[index].Field1 = value; 
    } else {
        this.filtered[index].Field2 = value; 
    }
    input.val(formatValue(value, 'input', !isPercent));
}

Ответ или решение

Почему v-model Vue не обновляется при использовании input.val(value)

При использовании Vue.js, важно понимать, что его система реактивности основывается на глубоких принципах JavaScript и синхронтации данных между бизнес-логикой и представлением. Когда вы прямым образом манипулируете DOM с помощью jQuery, Vue не осознает изменения, что становится причиной отсутствия обновления v-model. Давайте разберем детали данной проблемы и возможные решения.

Природа проблемы

Vue использует отдельные механизмы для отслеживания изменений в данных. Когда вы используете $(input).val(value), вы напрямую изменяете значение элемента DOM, что не вызывает реакцию Vue на это изменение. Это связано с тем, что Vue следит за изменениями в своем собственном состоянии, а не за тем, что происходит с элементами DOM, измененными в стороннем коде, например, с использованием jQuery.

Проблема с реактивностью

В вашем коде, после изменения значения поля ввода с помощью jQuery, Vue не получает уведомление о том, что данные были изменены. Таким образом, когда вы меняете значение поля, используя $(input).val(...), v-model не обновляется, так как Vue ожидает, что изменения будут происходить внутри его системы. Это и объясняет, почему в случае ручного ввода значения с клавиатуры, v-model обновляется коректно: в этом случае Vue отслеживает изменения.

Решение проблемы

Чтобы исправить проблему и обеспечить взаимосвязь между jQuery и Vue, вам нужно вручную обновлять данные в Vue, когда вы обновляете значения через jQuery. Это можно сделать следующим образом:

  1. Обновите данные в Vue: Вместо простого изменения значения в DOM с помощью jQuery, обновите соответствующее значение в массиве filtered, который вы используете в вашем v-model.

  2. Синхронизация данных: Когда вы копируете значения через copyValue, добавьте действие для обновления данных в Vue:

function copyValue(element, isPercent) { 
    var selector = isPercent ? '.mValue' : '.pValue';
    var row = $(element).closest('tr'); 
    var input = row.find(selector).first().children('input');
    var value = element.value.replace(/[^\d]+/g, '');
    value = converValue([value.slice(0, -2), '.', value.slice(-2)].join(''), isPercent);

    var index = row.index(); 
    if (isPercent) {
        // Обновление значения в Vue
        this.filtered[index].Field1 = value; 
    } else {
        // Обновление значения в Vue
        this.filtered[index].Field2 = value; 
    }

    // Теперь обновляем только DOM
    input.val(formatValue(value, 'input', !isPercent));
}

Пример реализации

Для обновления значений в полях Field1 и Field2 можно использовать следующие функции:

function updateValues(index) {
    // Обновляем значения в зависимости от вашей логики бизнес-правил
    var totalBudget = parseFloat(`@Model.TotalBudget`);
    var field1 = this.filtered[index].Field1;
    var field2 = this.filtered[index].Field2;

    this.filtered[index].Field2 = (field1 / 100 * totalBudget).toFixed(2);
    this.filtered[index].Field1 = (field2 / totalBudget * 100).toFixed(2);
}

Заключение

Таким образом, для корректной работы с данными в Vue и jQuery важно не забывать о том, что jQuery не обновляет реактивные данные Vue. Используя правильный подход и обновляя данные Vue при каждом изменении через jQuery, вы сможете обеспечить надежную и синхронизированную работу системы. Этот метод значительно улучшит ваш опыт работы с Vue и позволит вам оптимально использовать его возможности.

Оцените материал
Добавить комментарий

Капча загружается...