简体   繁体   中英

How to emit an event from Vue.js Functional component?

As the title of the question, this context is not available in the functional component. So if I have to emit an event, how can I do that?

For example in below code snippet:

<template functional>
    <div>
        <some-child @change="$emit('change')"></some-child>
    </div>
</template>

My functional component doesn't have this context and hence $emit is not available. How can I bubble-up this event?

Child Component

<template functional>
  <button @click="listeners['custom-event']('message from child')">
    Button from child
  </button>
</template>

Parent Component

<template>
  <div>
    <child-component @custom-event="call_a_method" />
  </div>
</template>

See it in action on codesandbox

Do you want to emit the event from the vue instance?

export default {
  functional: true,
  render(createElement, { listeners }) {
    return createElement(
      "button",
      {
        on: {
          click: event => {
            const emit_event = listeners.event_from_child;
            emit_event("Hello World!Is this the message we excpected? :/");
          }
        }
      },
      "Pass event to parent"
    );
  }
};

See it also a sandbox example here

This is explained in the docs Passing Attributes and Events to Child Elements/Components :

If you are using template-based functional components, you will also have to manually add attributes and listeners. Since we have access to the individual context contents, we can use data.attrs to pass along any HTML attributes and listeners (the alias for data.on ) to pass along any event listeners.

At the most basic level, you can delegate all listeners like this:

<some-child v-on="listeners"></some-child>

If you only want to bind the change listener, you can do:

<some-child @change="listeners.change"></some-child>

but this will fail if listeners.change is undefined/null (not provided to the functional component).

If you need to handle the situation where there is no change listener, then you can do this:

<some-child @change="listeners.change && listeners.change($event)"></some-child>

otherwise you would have to settle by writing the render function by hand, since I don't think it is possible to conditionally assign the change listener to <some-child> in the template of a functional component. (Or maybe you can? I'm not sure.)

If you want to pass event listener conditionally you can do it inside functional component template like this:

v-on="listeners.change ? { change: listeners.change } : null"

The issue of conditionally attaching listeners is discussed here

a component with jsx:

export default {
  name: "MyText",
  functional: true,// functional component
  props: {
    value: {
      type: [String, Number],
      default: ""
    }
  },
  render(h, context) {
    const { props } = context;

    // with jsx

    // return (
    //   <button
    //     onClick={() => {
    //       console.log(context.listeners);
    //       context.listeners.input(Math.random().toString(36));
    //       context.listeners["my-change"](Math.random().toString(36));
    //       context.data.on.change(Math.random().toString(36));
    //     }}
    //   >
    //     {props.value}
    //   </button>
    // );

    // or use h function
    return h(
      "h1",
      {
        on: {
         // emit some event when click h1
          click: () => {
            // has value prop has has input event auto
            // event name come  what event u listen in parent component
            console.log(context.listeners);
            context.listeners.input(Math.random().toString(36));
            context.listeners["my-change"](Math.random().toString(36));
            context.data.on.change(Math.random().toString(36));
          }
        }
      },
      props.value
    );
  }
};

conext.listeners is just an alias for context.data.on . in parent componet, you should listen my-change and change , or has error.

event name inside component comes what event u listen in parent component

<MyText
  v-model="value"
  @change="change"
  @my-change="myChange"
  @u-change="uChange"
/>

vue 2.6.11 works well.

see the codesandbox online

Parent:

<Child @onFunction="handleFunction">

and this is the child component:

Child

<template functional>
    <div>
        <some-child @change="execute"></some-child>
    </div>
</template>

methods:
  execute(){
   @emit("onFunction")
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM