proper use of Vue $refs
Many things changed from Vue.js 1.x to 2.x. I will walk you through the changes necessary in that snippet of yours:
v-repeat
should bev-for
- Replace
v-el="input"
withref="input"
- Since you are using
ref="input"
inside av-for
, thenthis.$refs.input
will be an array of elements, not a single element. - To access each single element, you will need an index (for the array), that's why you should include the
index
variable in thev-for
:v-for="(number, index) in numbers"
- Pass the
index
instead of theev
to the functions, so you can get the<input>
s later usingvm.$refs.input[index].focus();
- Since you are using
And that's pretty much it. After changes you'll get:
new Vue({
el: '#app',
data: {
numbers: [
{
val: 'one',
edit: false
},
{ val: 'two',
edit: false
},
{
val: 'three',
edit: false
}
]
},
methods: {
toggleEdit: function(index, number){
number.edit = !number.edit;
// Focus input field
var vm = this;
if (number.edit){
Vue.nextTick(function() {
vm.$refs.input[index].focus();
})
}
},
saveEdit: function(index, number){
//save your changes
this.toggleEdit(index, number);
}
}
})
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<div id="app">
<template v-for="(number, index) in numbers">
<span v-show="!number.edit"
v-on:click="toggleEdit(index, number)">{{number.val}}</span>
<input type="text"
ref="input"
v-model="number.val"
v-show="number.edit"
v-on:blur="saveEdit(index, number)"> <br>
</template>
</div>
If you want the functionality and not the code design, I'd recommend you redesign it. I think you want to edit data, and the data shouldn't have to know whether it's being edited. That is the role of a component.
So let's make a component that lets you v-model
data. The component itself has a span and an input. If you're editing, it shows the input, otherwise, the span. Click starts editing, blur stops editing. When editing starts, set focus on the input.
It takes a value
prop. Its input element emits an input
event to signal changes (per component v-model
spec.
new Vue({
el: '#app',
data: {
stuff: ['one', 'two', 'three']
},
components: {
inlineEditor: {
template: '#inline-editor-template',
props: ['value'],
data() {
return {
editing: false
}
},
methods: {
startEditing() {
this.editing = true;
this.$nextTick(() => this.$refs.input.focus());
},
stopEditing() {
this.editing = false;
}
}
}
}
});
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id="app">
<inline-editor v-for="item, index in stuff" v-model="stuff[index]"></inline-editor>
</div>
<template id="inline-editor-template">
<div>
<span @click="startEditing" v-show="!editing">{{value}}</span>
<input ref="input" :value="value" @input="e => $emit('input', e.target.value)" @blur="stopEditing" v-show="editing">
</div>
</template>