Search code examples
javascriptvue.jsvue-componentvuejs3vue-composition-api

Vue 3, call $emit on variable change


So I'm trying to build a component in Vue 3 that acts as a form, and in order for the data to be processed by the parent I want it to emit an object with all the inputs on change. The issue I'm having is that I don't seem to be able to call $emit from within watch() (probably because of the context, but I also don't see why the component-wide context isn't passed by default, and it doesn't accept this). I also cannot call any method because of the same reason.

I do see some people using the watch: {} syntax but as I understand it that is deprecated and it doesn't make a whole lot of sense to me either.

Here's a minimal example of what I'm trying to accomplish. Whenever the input date is changed, I want the component to emit a custom event.

<template>
  <input
    v-model="date"
    name="dateInput"
    type="date"
  >
</template>

<script>
import { watch, ref } from "vue";

    export default {
        name: 'Demo',
        props: {
        },
        emits: ["date-updated"],
        setup() {
          const date = ref("")

          watch([date], (newVal) => {
            // $emit is undefined
            console.log(newVal);
            $emit("date-updated", {newVal})
            // watchHandler is undefined
            watchHandler(newVal)
          })

          return {
            date
          }
        },
        data() {
            return {
            }
        },
        mounted() {
        },
        methods: {
          watchHandler(newVal) {
            console.log(newVal);
            $emit("date-updated", {newVal})
          }
        },
    }
</script>

Solution

  • Don't mix between option and composition api in order to keep the component consistent, the emit function is available in the context parameter of the setup hook::

    <template>
      <input
        v-model="date"
        name="dateInput"
        type="date"
      >
    </template>
    
    <script>
    import { watch, ref } from "vue";
    export default {
            name: 'Demo',
            props: {},
            emits: ["date-updated"],
            setup(props,context) {// or setup(props,{emit}) then use emit directly
              const date = ref("")
    
              watch(date, (newVal) => {
                context.emit("date-updated", {newVal}) 
              })
    
              return {
                date
              }
            },  
        }
    </script>
    

    if you want to add the method watchHandler you could define it a plain js function like :

    ...
     watch(date, (newVal) => {
                context.emit("date-updated", {newVal}) 
              })
    
      function watchHandler(newVal) {
                console.log(newVal);
               context.emit("date-updated", {newVal})
              }
    ...