Двусторонняя привязка свойств компонента… Флажок «Три насыщения»

#vue.js #vue-component

#vue.js #vue-компонент

Вопрос:

Я пытаюсь создать компонент с тройным флажком состояния. Изначально значение флажка равно нулю и представлено indeterminate флажком. Когда пользователь нажимает неопределенный флажок, значение становится true, затем нажимает false, а затем снова null.

Пока это то, что у меня есть:

 Vue.component('checkbox', {
        template: '<input type="checkbox" @change="change" class="checkbox-input">',
        props: ['currentSate'],
        mounted: function () {
            this.currentSate = null;
            this.$el.indeterminate = true
        },
        methods: {
            change: function () {
                if (this.currentSate == null) {
                    this.currentSate = true;
                    this.$el.checked = true;
                    this.$el.indeterminate = false;
                    return;
                }

                if (this.currentSate == true) {
                    this.currentSate = false
                    this.$el.checked = false;
                    this.$el.indeterminate = false;
                    return;
                }

                if (this.currentSate == false) {
                    this.currentSate = null;
                    this.$el.indeterminate = true;
                    return;
                }
                this.$emit('input', this.currentSate);
            }
        }
    });  
         <checkbox v-bind:current-sate="chState"></checkbox>
                chState = {{chState}}  

ошибка, которую я получаю, заключается в:

Избегайте прямого изменения реквизита, поскольку значение будет перезаписываться всякий раз, когда родительский компонент повторно отображает. Вместо этого используйте данные или вычисляемое свойство на основе значения prop. Изменяется реквизит: «currentSate»

Как это может быть достигнуто?

Ответ №1:

Когда вы получаете эту ошибку, в двух словах вам нужно ввести нужное значение, но пусть родительский элемент установит его так, чтобы оно передавалось обратно через свойство.

Подробная информация

Вы отправляете событие родительскому элементу с помощью @emit , родительский элемент изменяет свойство, привязанное к :value или :currentSate в вашем случае, а это, в свою очередь, обновляет дочерний элемент.

Способ v-model работы заключается в том, что входные данные изменяются в дочернем элементе, он запускает @input событие, родительский элемент получает его и изменяет привязку :value , и это возвращается к дочернему элементу.

Это кажется немного утомительным, но, в конце концов, устраняет целый ряд проблем с дизайном.

Решение

Итак, в вашем случае вам не следует изменять currentSate . Просто @emit новое значение и не изменять currentSate . Позвольте родительскому элементу изменить все, к чему data привязано :currentSate , и это изменит свойство currentSate .

Я советую вам использовать value вместо currentSate следования соглашениям Vue. Если вы создаете input и получаете value , v-model все будет работать «из коробки».

Для флажка «Три состояния»

Вы можете прослушать ‘click’, а затем включить значение свойства ‘currentSate’. Просто введите следующее значение на основе текущего значения. Вам все равно нужно разрешить родительскому устройству обновлять привязанное значение.

 onClick(value){
  switch(this.currentSate){
    case null: this.$emit('input', true); break;
    case true: this.$emit('input', false); break;
    case false: this.$emit('input', null); break;
  }
}
  

Вот пример прослушивания события и обновления связанного свойства:

 <checkbox v-bind:current-sate="chState" @input='chState=$event'></checkbox>              
  

Теперь, если вы создаете input и привязываете к :value вместо currentSate , вы можете сделать это:

 <checkbox v-model="chState"></checkbox>

  

v-model работает, когда вы используете соглашение о передаче вашего значения как input и принятии вашего свойства как value .

Комментарии:

1. Но мне нужно изменить текущее состояние, чтобы знать, какое значение присвоить ему при следующем нажатии

2. Я добавлю еще немного информации в решении.

3. Это можно упростить onClick() { this.$emit('input', this.currentState } }

4. Я больше не получаю ошибку, но на родительском сервере ничего не происходит, можете ли вы опубликовать полный пример js / html? Может быть, на jsfiddle или что-то в этом роде?

5. @boruchsiper Я обновил решение с помощью логики, позволяющей изменять исходное значение свойства.

Ответ №2:

Предупреждение появляется, потому что вы изменяете prop непосредственно из дочернего компонента.

v-model это, безусловно, правильный путь, однако управление состоянием может быть выполнено гораздо более элегантным способом.

Я бы рекомендовал использовать пользовательскую директиву для принудительного применения indeterminate состояния флажка при его вставке в DOM. Здесь вы можете найти ручку с рабочим примером.

В двух словах это будет выглядеть так:

 Vue.directive('indeterminate', (el, binding) => {
  if (el.type amp;amp; el.type === "checkbox" amp;amp; binding.value === null) {
    el.indeterminate = true;
  }
});

new Vue({
  el: '#app',
  data: () => ({
    checked: null,
  }),
  methods: {
    onInput($event) {
      this.checked = this.checked === false ? null : this.checked === true ? false : true;
    }
  }
})  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  I am {{ checked === null ? 'NULL' : checked }}
  <input type="checkbox" v-indeterminate="checked" :checked="checked" @input="onInput">
</div>  

Обновить:

Обновлено для обработки трех состояний в цикле. Простым решением является обработка последовательности состояний в onInput и обновление директивы при каждом изменении.

Комментарии:

1. Флажок никогда не возвращается в неопределенное состояние при последующих щелчках в этом примере.

2. Верно @boruchsiper, я обновил ответ, чтобы сохранить минимальный код без временного состояния

3. Обновление работает, но логика происходит в приложении, а не в дочернем компоненте. Предполагается, что это готовый компонент с трехпозиционным флажком.

4. Хорошо, я думаю, вы здесь упускаете суть. Я предлагаю использовать директиву для обработки состояния, а не использовать компонент для переноса только флажка. Перемещение логики в компонент является тривиальным. Директивы — это средства расширения атрибутов HTML для соответствия подходу, основанному на данных, компонент для этого является излишеством.

Ответ №3:

Ни один из ответов не был полным, так что вот он.

 Vue.component('checkbox', {
        template: '<input type="checkbox" @change="change" class="checkbox-input">',
        props: ['value'],
        mounted: function () {
            this.value = null;
            this.$el.indeterminate = true
        },
        methods: {
            change: function () {
                if (this.value == null) {
                    this.$emit('input', true);
                    return;
                }

                if (this.value == true) {
                    this.$emit('input', false);
                    return;
                }

                if (this.value == false) {
                    this.$el.indeterminate = true;
                    this.$emit('input', null);

                    return;
                }
            }
        }
    });

    var myApp = new Vue(
        {
            el: '#app',
            data: {
                chState: null,
            }
        });  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
 <checkbox v-model="chState"></checkbox>
        chState = {{chState}}
</div>