Home > Net >  Vue.js 2: converting a string in a function call at an event handler
Vue.js 2: converting a string in a function call at an event handler

Time:09-24

In Vue.js 2 I would like to convert a string into a function call so that it can be set as an event handler. I believe this would be very practical, specially when dynamically creating lots of elements (e.g. buttons) based on a list of objects.

new Vue({
  el: "#app",
  data: {
    myArray: [
      { value: 1, fn: "firstMethod" },
      { value: 2, fn: "secondMethod" },
    ],
  },
  methods: {
    firstMethod() {
      console.log("'firstMethod' was executed.");
    },
    secondMethod() {
      console.log("'secondMethod' was executed.");
    },
  },
});
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <template v-for="elem in myArray">
        <button @click="elem.fn">  <!-- Here is where I am stucked. -->
          <!-- <button> -->
          {{elem.value}}
        </button>
      </template>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script src="script.js"></script>
  </body>
</html>

My first attempt at doing this was setting the fn properties in myArray as sort of pointers to the corresponding functions with this (e.g. fn: this.firstMethod). The problem is, I believe, that at the time of the definition, these functions are still unkown, as I get: [Vue warn]: Invalid handler for event "click": got undefined.

Is what I am trying to achieve even possible? Is there a downside with this strategy that I am overlooking?

CodePudding user response:

Try to create one method, which will be working with all buttons

new Vue({
  el: "#app",
  data: {
    myArray: [
      { value: 1, fn: "firstMethod" },
      { value: 2, fn: "secondMethod" },
    ],
  },
  methods: {
    basicMethod(name) {
      console.log(`'${name}' was executed.`);
      if(name === 'firstMethod') {
        //some logic, and so on for other methods if u need
      }
    },
  },
});
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <template v-for="elem in myArray">
        <button @click="basicMethod(elem.fn)">  <!-- Here is where I am stucked. -->
          <!-- <button> -->
          {{elem.value}}
        </button>
      </template>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script src="script.js"></script>
  </body>
</html>

CodePudding user response:

You can use a generic method provided with the function name the call this[ fn ]();.

But for security reasons, you might want these custom methods to be in an object, not just on the main this, so other methods can't be called.

Also, you want to check if the method exists before calling it.

It would look something like this:

new Vue({
  el: "#app",
  data: {
    myArray: [
      { value: 1, fn: "firstMethod" },
      { value: 2, fn: "secondMethod" },
      { value: 3, fn: "nonExistingMethod" }, // Won't throw an error
      { value: 4, fn: "someImportantSecureMethod" }, // Won't be called
    ],
    customMethods: {
      firstMethod: function() {
        console.log("'firstMethod' was executed.");
      },
      secondMethod: function() {
        console.log("'secondMethod' was executed.");
      },
    },
  },
  methods: {
    callCustomMethod(fn) {
      // Make sure it exists
      if (typeof this.customMethods[fn] === "function") {
        // Only methods inside the customMethods object are available
        this.customMethods[fn]();
      }
    },
    someImportantSecureMethod() {
      console.log('The method may not be exposed to dynamic calling!');
    },
  },
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
    <template v-for="elem in myArray">
        <button @click="callCustomMethod(elem.fn)">
            <!-- <button> -->
            {{elem.value}}
        </button>
    </template>
</div>

As a side note: You might also considering using custom events (see docs) for this. Using $emit('custom-event-name') as the v-on:click handler and have your custom methods as event listeners. (Makes it easy when you later might want to make the items into separate components.)

  • Related