Vue не обновляет вычисляемое свойство на основе метода, но тот же метод возвращает правильное значение при прямом вызове

У меня есть простой компонент кнопки для отправки форм, и он должен давать небольшую обратную связь при нажатии на него: показать счетчик и отключить кнопку во время отправки формы, а затем также отключить его, если с сервера возвращаются какие-либо ошибки. до тех пор, пока ошибки не будут устранены (ошибки устранены путем изменения ввода полей, в которых они есть).

Вот шаблон компонента:

<template>
    <button type="submit" :disabled="disabled">
        <loading-spinner v-if="submitting"></loading-spinner>
        <slot>Submit</slot>
    </button>
</template>

<script>
    export default {
        props: {
            form: {
                type: Object,
                required: true
            }
        },
        created() {
            // Globally available event handler which listens for events 
            // emitted by Form object - it works fine
            Event.$on('submitting', () => this.submitting = true);
            Event.$on('submitted', () => this.submitting = false);
        },
        data() {
            return {
                submitting: false,
            }
        },
        computed: {
            // Here is the problem: this.form.errors.any() doesn't get updated in the 
            // computed prop even when errors are cleared. This same method, however,
            // returns correct result when called directly via a test button
            disabled() {
                return this.submitting || this.form.errors.any()
            },
              // doesn't get updated either - apparently
//            errors() {
//                return this.form.errors.any()
//            }
        },
    }
</script>

Кнопке передается объект Form, поэтому он также может иметь доступ к объекту Errors, у которого есть несколько удобных методов для управления ошибками. Объект Form, который передается в качестве опоры, является реактивным внутри компонента submit-button, и его поля обновляются в Vue Devtools по мере ввода в форму (в родительском компоненте), но вычисляемая опора (disabled или errors), которая полагается на нее, не получать обновления.

Когда я отправляю форму с неверными данными, я получаю ошибки, поэтому form.errors.any() возвращает true. Однако, как только ошибка возвращается в первый раз, кнопка становится неактивной и вот так зависает (form.errors.any() продолжает возвращать истину, даже если это не так).

Я знаю, что вычисленные реквизиты кэшируются, но по определению они должны обновляться при обновлении их зависимости (в этом случае вычисляемое свойство disabled должно обновляться, потому что обновляется this.form.errors.any()). Однако кажется, что компонент кнопки отправки игнорирует обновление this.form.errors.any() в своем вычисляемом свойстве (он продолжает возвращать истину при наличии ошибок и никогда не обновляется). То же самое происходит, если я создаю такое же вычисляемое свойство для родительского компонента.

Дополнительная информация

Когда я вручную проверяю его значение (например, с помощью кнопки, вызывающей form.errors.any()), он каждый раз записывает правильное значение в консоль, но не обновляется в моем вычисляемом свойстве.

Метод any() на объекте Errors - здесь ничего особенного

any() {
    return Object.keys(this.errors).length > 0;
}

И объект Form, и объект Errors работают нормально: их методы возвращают нормальные значения при прямом вызове (например, посредством нажатия кнопки) из любого компонента, и они отправляют события, как ожидалось, они просто не реагируют на вычисленную опору.

Временное решение

Решением для меня было использование метода вместо вычисляемого свойства, но это слишком многословно, и я в основном хотел бы знать, почему в этом случае я не могу использовать более элегантное вычисляемое свойство?

<template>
    <button type="submit" :disabled="submitting || anyErrors()">
        <loading-spinner v-if="submitting"></loading-spinner>
        <slot>Submit</slot>
    </button>
</template>

<script>
    export default {
        ...

        methods: {
            anyErrors() {
                return this.form.errors.any()
            }
        }
    }
</script>


person tal3nce    schedule 10.08.2020    source источник
comment
Я бы сказал, что это вызвано изменением объекта способом, который Vue не может обнаружить (см. документы). Как вы заполняете ошибки в объекте Errors? Вы добавляете объекту новые свойства?   -  person Decade Moon    schedule 10.08.2020
comment
@DecadeMoon ошибки - это объект внутри класса Errors. Однако, если вы спросите, это не компонент Vue (поскольку я понял, что об этом говорится в главе в документации). this.errors - это пустой объект при установке (конструктор), и к нему можно получить доступ / заполнить / очистить с помощью методов (получить / зарегистрировать / очистить и уже упоминалось).   -  person tal3nce    schedule 10.08.2020


Ответы (1)


Вам нужно разместить свойство form.errors на реквизитах для формы, чтобы оно реагировало из коробки. В противном случае, когда вы добавляете ошибки, вы должны использовать метод Vue.set(), чтобы сделать его реактивным.

Итак, ваш объект props должен выглядеть так:

props: {
     form: {
         type: Object,
         required: true,
         errors: {},
     }
},

Дополнительная информация: https://vuejs.org/v2/guide/reactivity.html

(отредактировано, чтобы сделать ошибки объектом, как указано в вопросе, а не массивом)

person Ashley H.    schedule 10.08.2020
comment
по-прежнему не работает с вычисляемым свойством. Я также попытался передать только объект ошибок (вместо всей формы, поскольку он мне на самом деле не нужен, я использую только ошибки из него), но он его не обнаруживает - person tal3nce; 10.08.2020
comment
Вы пробовали использовать Vue.set()? Посмотрите ссылку, которую я разместил. Мне все еще кажется, что это проблема с реактивностью, поскольку данные там просто не обновляются на экране. - person Ashley H.; 11.08.2020
comment
Привет, @Ashley. Да, я пробовал, но все равно не работает. Это может быть потому, что я неправильно его настраиваю. Должен ли я вызвать this. $ Set в ловушке жизненного цикла created / connected? Насколько я понял, вы вызываете объект, для которого хотите установить реактивность, присваиваете ему свойство и значение. Это должно выглядеть примерно так: this. $ Set (this.form.errors, 'hasErrors', this.form.errors.any ())? Но новое свойство hasErrors все еще не реагирует. Я, должно быть, здесь что-то не так, не могли бы вы объяснить это поподробнее, пожалуйста? Спасибо и извините за столь поздний ответ! - person tal3nce; 23.08.2020