Довольно часто встречается задача реализации ин-плейс-редактирования. То есть когда кликнули например два раза на пункт списка, и текст тут же заменился редактором, мы его поменяли, нажали enter, сохранили.
Сделать это во Vue.js несложно: рядом с каждым редактируемым пунктом выводится инпут, и в v-show
указываются противоположные условия. А по двойному клику значение условия изменяется. Например:
<template>
<!-- ... -->
<ul>
<li v-for="(item, index) in items" :key="item.id">
<div @dblclick="editTitleMode(item)">
<div v-show="!item.edit_mode">{{ item.title }}</div>
<div v-show="item.edit_mode">
<input v-model="item.title" @keyup.enter="saveItemTitle(item)" type="text" />
</div>
</div>
</li>
</ul>
</template>
<script>
export default {
data() {
return {
items: []
}
},
methods {
editTitleMode: function (item) {
this.$set(item, 'edit_mode', true);
},
saveItemTitle: function (item) {
// Do the saving
item.edit_mode = false;
}
}
}
</script>
И что еще хотелось бы сделать в этой ситуации — так это каждый раз, когда появляется инпут для редактирования, автоматически ставить на него фокус. В принципе, с этим можно легко справиться с помощью jQuery, зная, какой инпут сейчас будет показан. Но когда работаешь с Vue.js, каждое обращение к jQuery в коде кажется малодушием. И в данном случае прекрасно можно обойтись без jQuery.
У компонентов Vue.js есть массив this.$refs
, содержащий элементы с заданным атрибутом ref
. Тогда найти нужный элемент и поставить на него фокус можно вот так:
<template>
<!-- ... -->
<ul>
<li v-for="(item, index) in items" :key="item.id">
<div @dblclick="editTitleMode(item)">
<div v-show="!item.edit_mode">{{ item.title }}</div>
<div v-show="item.edit_mode">
<input :ref="'input_item_' + item.id" v-model="item.title" @keyup.enter="saveItemTitle(item)" type="text" />
</div>
</div>
</li>
</ul>
</template>
<script>
export default {
// ...
methods {
editTitleMode: function (item) {
this.$set(item, 'edit_mode', true);
this.$refs['input_item_' + item.id][0].focus();
},
saveItemTitle: function (item) {
// Do the saving
item.edit_mode = false;
}
}
}
</script>
Правда, если добавить этот код в метод, по которому показывается инпут, установки фокуса не произойдет. Всё дело в том, что в момент вызова метода editTitleMode()
, значение item.edit_mode
, используемое в v-show
уже изменилось, но Vue.js еще не выполнил цикл проверки реактивных переменных и соответствующие действия. То есть, сам инпут все еще невидим и не может получить фокус.
Чтобы заработало, нужно ставить фокус позже, после цикла проверки, с помощью метода nextTick()
вот так:
editTitleMode: function (item) {
this.$set(item, 'edit_mode', true);
this.$nextTick(() => {
this.$refs['input_item_' + item.id][0].focus();
});
}
В качестве бонуса — поставим курсор в конец текста в инпуте так, чтобы его (курсор) было видно:
editTitleMode: function (item) {
this.$set(item, 'edit_mode', true);
this.$nextTick(() => {
let element = this.$refs['input_item_' + item.id][0];
let position = element.value.length;
element.focus();
if (element.setSelectionRange) {
element.setSelectionRange(position, position);
} else if (element.createTextRange) {
let range = element.createTextRange();
range.collapse(true);
range.moveEnd('character', position);
range.moveStart('character', position);
range.select();
}
});
}