2

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?

8
  • i don't think what you are trying to achieve is possible and in my own view this doesnt look dynamic in any way Commented Sep 24, 2021 at 11:31
  • 2
    why not just have a single function that would have a different output based on the parameter passed into the function and give those unique parameter to each object in your myArray Commented Sep 24, 2021 at 11:34
  • Shouldn't you write functions fn: "firstMethod" without the quotes? Otherwise it's a string, correct? So fn: firstMethod should reference the method itself. Not this. as this is not neccessary in the template. I would try that. Commented Sep 24, 2021 at 11:41
  • 1
    @rubebop yeah the function would get bulky ... can you give me a scenario where you would be using this approach .. let me see if i can provide a better approach Commented Sep 24, 2021 at 11:58
  • 1
    @rubebop Oh that's right when you reference the function during creation of the data then the methods do not exist yet. You can stick with the solution you found. Another way I found is to override create() and assign the functions to-be-called there. But that's probably not self-explanatory enough. Commented Sep 24, 2021 at 12:32

2 Answers 2

1

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>

Sign up to request clarification or add additional context in comments.

2 Comments

Yes, this was the solution that @onifadeboluwatife suggested in the previous comments. It definitely works fine. The only downside is that, when having lots of elements, the function can get bulky. I also guess from the answers, that my original approach is not possible.
@rubebop for example, you can move the logic of each function into a separate file
1

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.)

1 Comment

Thanks! That makes sense (I also sort of thought of this solution, but I missed the this[ fn ]() thing). I also appreciate the input concerning the security aspect and lastly the idea of emitting custom events.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.