Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

proper use of Vue $refs

Im attempting to recreate this exact inline editing functionality in on of my vue components. However, and I may be wrong, I see some of the syntax is outdated Vue, in particular the v-el directive being used. I've attempted to update the syntax like so:

new Vue({
  el: '#app',
  data: {
    numbers: [{
        val: 'one',
        edit: false
      },
      {
        val: 'two',
        edit: false
      },
      {
        val: 'three',
        edit: false
      }
    ]
  },

  methods: {
    toggleEdit: function(ev, number) {
      number.edit = !number.edit

      // Focus input field
      if (number.edit) {
        Vue.nextTick(function() {
          ev.$refs.input.focus(); // error occurs here
        })
      }
    },

    saveEdit: function(ev, number) {
      //save your changes
      this.toggleEdit(ev, number);
    }
  }
})
<div id="app">
  <template v-for="number in numbers">
        <span v-show="!number.edit"
              v-on:click="toggleEdit(this, number)">{{number.val}}</span>

        <input type="text"
               ref="input"
               v-model="number.val"
               v-show="number.edit"
               v-on:blur="saveEdit(ev, number)"> <br>
    </template>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>

However I get a range of errors... any suggestions on how to properly execute this?

Here is the Error:

[Vue warn]: Error in nextTick: "TypeError: undefined is not an object (evaluating 'ev.$refs.input')"

like image 841
John Durand Avatar asked Mar 01 '18 00:03

John Durand


People also ask

What is use of $refs in Vue?

Ref s are Vue. js instance properties that are used to register or indicate a reference to HTML elements or child elements in the template of your application. If a ref attribute is added to an HTML element in your Vue template, you'll then be able to reference that element or even a child element in your Vue instance.

What is the use of Vue use?

VueJS is primarily used to build web interfaces and one-page applications. In saying that, it can also be applied to both desktop and mobile app development thanks to the HTML extensions and JS base working in tandem with an Electron framework – making it a heavily favoured frontend tool.

How do you use nextTick Vue?

The nextTick() function allows you to execute code after you have changed some data and Vue has updated the page to reflect your changes. Pass a callback to nextTick() and Vue will execute the callback immediately after updating the DOM.

How do I use Vue components?

Open the file in your code editor. Create the component's template section by adding <template></template> to the top of the file. Create a <script></script> section below your template section. Inside the <script> tags, add a default exported object export default {} , which is your component object.


2 Answers

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 be v-for
  • Replace v-el="input" with ref="input"
    • Since you are using ref="input" inside a v-for, then this.$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 the v-for: v-for="(number, index) in numbers"
    • Pass the index instead of the ev to the functions, so you can get the<input>s later using vm.$refs.input[index].focus();

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>
like image 124
acdcjunior Avatar answered Nov 16 '22 01:11

acdcjunior


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>
like image 24
Roy J Avatar answered Nov 16 '22 00:11

Roy J