MartenCatcher
MartenCatcher

Reputation: 2887

Use current vue component's method as default prop value

I am going to pass function as property to Vue component. Let it be function has to be invoked by @click. But for some reasons I want to keep default behaviour for the component. The default behaviour is not so easy as I want and I'm going to use method as default function. Because the default behaviour requires data from component's state (props, data, other methods, whatever).

Is there a way to do?

I've attached the example. Expected behaviour:

Button works fine should produce alert You are welcome!
Button nope should produce alert You are welcome as well! but does nothing.

Vue.component('welcome-component', {
  template: '#welcome-component',
  props: {
    name: {
      type: String,
      required: true,
    },
    action: {
      type: Function,
      required: false,
      default: () => { this.innerMethod() },
    },
  },
  methods: {
    innerMethod() {
      alert("You are welcome as well!");
    }
  }
});


var app = new Vue({
  el: "#app",
  methods: {
    externalMethod() {
      alert("You are welcome!");
    }
  }
});
#app {
  margin: 20px;
}

.holder > button {
  margin: 10px;
  padding: 5px;
  font-size: 1.1em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <welcome-component name="works fine" :action="externalMethod"></welcome-component>
  <welcome-component name="nope"></welcome-component>
</div>


<script id='welcome-component' type='x-template'>
  <div class="holder">
    <button @click="action">{{ name }}</button>
  </div>
</script>

Upvotes: 6

Views: 5521

Answers (2)

mynd
mynd

Reputation: 794

From vue docs:

"Note that props are validated before a component instance is created, so instance properties (e.g. data, computed, etc) will not be available inside default or validator functions." (https://v2.vuejs.org/v2/guide/components-props.html)

Referencing the innerMethod is therefore one of the cases where the components function is not yet available.

Idea: If it is crucial for you to have this kind of functionality you could check within a later lifecycle hook (like created, mounted etc.) whether the type of action is function. If it's not a function (meaning not passed via prop), assign the innerMethod manually.

Upvotes: 7

skirtle
skirtle

Reputation: 29092

This works but it's a total bag of spanners:

action: {
  required: false,
  default () { 
    return () => this.innerMethod();
  },
},

I've had to remove the type: Function. Usually when default is a function it will be invoked to return the appropriate default value. However, if the prop has type: Function it'll just treat the function as the default. In this case that's problematic because we lose the this binding.

The internal arrow function is required to get around the problem that the methods aren't available when the default function is called.

If possible I would suggest giving up on using a default and instead just apply the 'default' when it needs to be invoked. So rather than calling action directly in the click handler, instead call a method called invokeAction that looks something like this:

invokeAction () {
  if (this.action) {
    this.action();
  } else {
    this.innerMethod();
  }
}

Upvotes: 8

Related Questions