Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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>
like image 989
MartenCatcher Avatar asked Aug 01 '19 16:08

MartenCatcher


People also ask

Can I pass function as props Vue?

You can pass strings, arrays, numbers, and objects as props. But can you pass a function as a prop? While you can pass a function as a prop, this is almost always a bad idea. Instead, there is probably a feature of Vue that is designed exactly to solve your problem.

How do you pass a component as a prop Vue?

To pass a component as props and use it in a child component in Vue. js, we can use the component component. to add the component component. We set the is prop to the childComponent string which has the name of the component we want to render.

Are props required by default Vue?

All props are optional by default, unless required: true is specified. An absent optional prop other than Boolean will have undefined value. The Boolean absent props will be cast to false .


2 Answers

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.

like image 96
mynd Avatar answered Oct 11 '22 03:10

mynd


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();
  }
}
like image 28
skirtle Avatar answered Oct 11 '22 03:10

skirtle