#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>