1

I have been looking around and found that the most common solution to my problem is to set a variable with the instance of the app/component and then using this variable to change the data (Example 1, Example 2).

My issue is that I cannot use arrow functions (I am only allowed to use ES5 comptible JS, not my call) and that my function does not have a "previous" part to set the app instance.

var theApp = new Vue({
    el: '#the-app',
    data: {
        selectedView: 1,
        myDynamicButtonsArray: [
            {
                text: 'Change view!!'
                onClick: function () {
                    // here this.selectedView should be changed
                    this.selectedView = 2;
                }
            },
            {
                text: 'My other button'
                onClick: function () {
                    // Here something completely unrelated should happen
                }
            }
        ]
    }
});

The I loop over myDynamicButtonsArray and load a myButton component that executes the onClick when clicked.

<my-button v-for="button in myDynamicButtonsArray" v-bind="button"></my-button>

The problem is that when I execute this.selectedView = 2; the this is not refering to the app instance but the function instance where it is being executed, makes sense.

I have tried setting the value like this:

theApp._data.selectedView = 2;

But I am not sure if this is the right approach.

The reason why I am not using the $emit is because there are many different functions to be executed, and emiting a button-clicked and then executing a common function with a giant switch so it does one thing or another depending on what button was pressed does not seem like a viable solution:

// MyButton component template
<button @onClick="this.$emit('button-clicked', id)">
    {{ text }}
</button>

// App html
<my-button 
    v-for="button in myDynamicButtonsArray" 
    v-bind="button"
    @button-clicked="buttonClicked"
>
</my-button>

// App object
var theApp = new Vue({
    el: '#the-app',
    data: {
        selectedView: 1,
        myDynamicButtonsArray: [
            {
                text: 'Change view!!',
                id: 1
            },
            {
                text: 'My other button',
                id: 2
            }
        ]
    },
    methods: {
        buttonClicked: function(id) {
            switch(id) {
                case 1:
                    this.selectedView = 2; 
                    // I am not even sure if I can use 'this' the way I want here;
                    break;
                case 2:
                    // And so on for all my buttons
            }
        }
    }
});

And emitting a different event per button does also not seem viable:

// MyButton component template
<button @onClick="this.$emit(customEventString)">
    {{ text }}
</button>

// App html
<my-button 
    v-for="button in myDynamicButtonsArray" 
    v-bind="button"
    @custom-event-1="customEvent1"
    @custom-event-2="customEvent2"
>
</my-button>

// App object
var theApp = new Vue({
    el: '#the-app',
    data: {
        selectedView: 1,
        myDynamicButtonsArray: [
            {
                text: 'Change view!!',
                customEventString: 'custom-event-1'
            },
            {
                text: 'My other button',
                customEventString: 'custom-event-2'
            }
        ]
    },
    methods: {
        customEvent1: function() {
            this.selectedView = 2; 
            // I am not even sure if I can use 'this' the way I want here;
        },
        customEvent2: function() {
            // Do other things
        }
    }
});

My question is, which is the right approach:

  • Using myApp._data
  • Having one event and a switch inside
  • Or something else
1
  • Would it be possible to move the onClick function to vue's methods and rename it to eg "myButtonOne" or something. Then have the function name as string inside the myDynamicButtonsArray instead. This way you could run the function on the button like so: @click="this['myButtonOne']()"? Commented Dec 18, 2019 at 11:27

2 Answers 2

2

First you need to return a function off your data property and second bind the this keyword to the function

Example:

const MyButton = Vue.component('my-button', {
  template: '<button v-text="text" @click="onClick"></button>',
  props: ['text', 'onClick']
});

new Vue({
  el: '#the-app',
    
  components: { MyButton },
  
  data: function() {
    return {
      selectedView: 1,
      buttons: [
        {
          text: 'Change view!!',
          onClick: function () {
            console.log('btn 1 clicked')
            // here this.selectedView should be changed
            this.selectedView = 2;
          }.bind(this)
        },
        {
          text: 'My other button',
          onClick: function () {
            console.log('btn 2 clicked')
            // Here something completely unrelated should happen
          }
        }
      ]
    }
  }
});
<div id="the-app">
  {{ selectedView }}
  <my-button v-for="(button, index) in buttons" :key="index" v-bind="button" />
</div>


<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>

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

1 Comment

Accepted this one because I have a several nested components, and with this solution it works perfectly.
1

You can change what this means for any function using bind()

<button
      v-for="button in myDynamicButtonsArray"
      @click="bindToMe(button.onClick)()"
      :key="button.text"
    >{{ button.text }}</button>
methods: {
    bindToMe(handler) {
      return handler.bind(this);
    }
  }

Just note about syntax inside @click - instead of passing handler directly, we are calling a function which returns original handler but with this bound to the current Vue instance....

Demo

Comments

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.