Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue triggering function with params from child unexpected

Tags:

vue.js

vuejs2

I have the following component:

<template>
  <button class="button" @[click]="action">{{ text }}</button>
</template>

<script>
export default {
  name: "Button",
  props: {
    action: {
      type: Function,
      required: false
    },
    text: {
      type: String,
      required: true
    },
    inputType: {
      type: String,
      required: false
    }
  },
  computed: {
    click() {
      return this.action ? "click" : null;
    }
  }
};
</script>

However when I pass a function as the action with a parameter then the function is already triggered on render. Without a parameter it works fine.

<v-button inputType="button" :action="say('Hello')" text="Alert" />
<v-button inputType="button" :action="say" text="Alert" />

The triggered function:

say(message) {
    alert(message);
}

You can see the behaviour here. Looking at this I would expect it to work with passing params.

So my question is how can I prevent the trigger on render?

like image 562
SuperDJ Avatar asked Jan 24 '26 03:01

SuperDJ


1 Answers

Using $emit

Instead of passing a function into the child component, you could instead emit a clicked event; e.g:

<button class="button" @click="$emit('clicked')">{{ text }}</button>

Then listen to the emitted event on the component itself, triggering your function:

<v-button inputType="button" @clicked="say('Hello')" text="Alert" />

Although emitting and handling events is a neat way to communicate from a child component to its parent, it can break down somewhat when the component emitting the event is not a direct descendant; e.g: where the component is a grandchild.

Vue does not implicitly bubble events up through the component tree, I believe this is by design to ensure event behaviour is explicit.

Using <slot>

In this case, it's often desirable to use slots to create a component that as access to the scope in which it was created, but then nest it within another child component.

<modal>
  <v-button @clicked="say('hi)" text="Alert"/>
</modal>

Using a higher-order function

Otherwise, if you need to pass a function to a child component, and that function also has an argument, you'll have to create it as a higher-order function.

In your case, you want to pass the say method to a child, with an argument. You want to pass the function but not invoke it, but when you pass an argument to say() you are invoking it there and then:

<v-btn :action="say('hi')">

The solution here is to rewrite say so it also returns a function, which will then be invoked when the button is clicked:

say (message) {
  return () => alert(message)
}

This implies that you must invoke the say method when passing it to the button component even if you are not passing a message to that button instance. So, the following will work:

<v-button :action="say()" text="Alert"/>

But this will not because it does not invoke the inner function:

<v-button :action="say" text="Alert"/>

Hope this helps :)

like image 155
Darragh Enright Avatar answered Jan 26 '26 17:01

Darragh Enright



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!