What's new with event handling in Vue 3.0?

Looking for a way to handle an event in a child component, check for a specific condition, and then emit the "submit" event back to the parent for its event handler to run. The code snippet below demonstrates this process using Vue.js 2.6.11 (replacing "vue" with "@vue/composition-api"). However, when moving to version 3.0.0-rc.5, the parent's event handler seems to trigger twice. I'm curious to know if this behavior change is intentional, a bug, or something on my end.

Here is the structure of the components:

<template lang="pug">
#app
  .container
    h1 Form Experiment
    FormGroup(@submit='onSubmit')
      button.btn.btn-primary(type='submit') Submit
</template>

<script lang="ts">
import { defineComponent } from "vue"
import FormGroup from "@/components/FormGroup.vue"

export default defineComponent({
  name: "App",
  components: {
    FormGroup,
  },
  setup() {
    const onSubmit = (): void => {
      alert("Parent event handler")
    }
    return { onSubmit }
  }
})
</script>

FormGroup.vue:

<template lang="pug">
form(@submit.prevent='onFormSubmit', novalidate, autocomplete='on')
  slot Content needed in FormGroup
</template>

<script lang="ts">
import { defineComponent } from "vue"

export default defineComponent({
  name: "FormGroup",
  setup(_, { emit }) {
    const check = ref(true) // evaluated elsewhere - simplified for this example
    const onFormSubmit = (): void => {
      if (check.value) {
        alert("Form is valid - sending event to parent")
        emit("submit")
      } else {
        alert("Form is not valid.") // do not emit the event to parent
      }
    }

    return { check, onFormSubmit }
  }
})
</script>

Any thoughts on why onSubmit() in the parent triggers twice in Vue.js 3.0.0-rc.5?

Answer №1

Seems like the code is specifically for Vue 3

inheritAttrs

Type: boolean

Default: true

Details:

By default, parent scope attribute bindings that are not recognized as props will "fallthrough". This means that when we have a single-root component, these bindings will be applied to the root element of the child component as normal HTML attributes. When authoring a component that wraps a target element or another component, this may not always be the desired behavior. By setting inheritAttrs to false, this default behavior can be disabled. The attributes are available via the $attrs instance property and can be explicitly bound to a non-root element using v-bind.

Note: this option does not affect class and style bindings.

docs: https://v3.vuejs.org/api/options-misc.html#inheritattrs

Should be working (added inheritAttrs: false):

<template lang="pug">
form(@submit.prevent='onFormSubmit', novalidate, autocomplete='on')
  slot Content needed in FormGroup
</template>

<script lang="ts">
import { defineComponent } from "vue"

export default defineComponent({
  name: "FormGroup",
  inheritAttrs: false,
  setup(_, { emit }) {
    const check = ref(true) // evaluated elsewhere - simplified for this example
    const onFormSubmit = (): void => {
      if (check.value) {
        alert("Form is valid - sending event to parent")
        emit("submit")
      } else {
        alert("Form is not valid.") // so don't emit the event to parent
      }
    }

    return { check, onFormSubmit }
  }
})
</script>

const {
  defineComponent,
  createApp,
  ref,
} = Vue

const FormGroupFactory = (name, inheritAttrs) => defineComponent({
  name,
  inheritAttrs,
  setup(_, {
    emit
  }) {
    const onFormSubmit = () => {
      emit("evt", "@EVT")
      emit("submit", "@SUBMIT")
    }
    return {
      onFormSubmit
    }
  },
  template: document.getElementById("FormGroup").innerHTML
})


createApp({
    el: '#app',
    components: {
      FormGroupOne: FormGroupFactory('FormGroupOne', true),
      FormGroupTwo: FormGroupFactory('FormGroupTwo', false),
    },
    setup() {
      const log = ref('');
      const onSubmit = (e) => {
        log.value = log.value + "Parent event handler" + e + "\n"
      }
      return {
        log,
        onSubmit
      }

    }
  })
  .mount('#app')
<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="1167647451223f213f213c63723f24">[email protected]</a>/dist/vue.global.js"></script>

<div id="app">
  <div class="container">
    <form-group-one @submit="onSubmit"><button class="btn btn-primary" type="submit">Submit</button> inheritAttrs: true</form-group-one>
    <form-group-two @submit="onSubmit"><button class="btn btn-primary" type="submit">Submit</button> inheritAttrs: false</form-group-two>
  </div>
  <pre>{{log}}</pre>
</div>

<template id="FormGroup">
  <form @submit.prevent="onFormSubmit" novalidate="novalidate" autocomplete="on">
    <slot>Content needed in FormGroup</slot>
  </form>
</template>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Executes the function in the child component only if the specified condition evaluates to true

When a specific variable is true, I need to call a function in a child component. If the variable is false, nothing should happen. allowDeleteItem = false; <ChildComponent .... removeItemFn={ deleteFn } /> I attempted to use the boolean variable wi ...

Tips for passing an object as an argument to a function with optional object properties in TypeScript

Consider a scenario where I have a function in my TypeScript API that interacts with a database. export const getClientByEmailOrId = async (data: { email: any, id: any }) => { return knex(tableName) .first() .modify((x: any) => { if ( ...

What is the correct way to utilize a variable as a parameter in React Query while employing the axios.request(options) method?

I'm currently working on a React Query component with the following structure: function test () { const [var, setVar] = useState("") const options = { method: "GET", url: "https://api.themoviedb.org/3/search/tv" ...

The global variable in TypeScript is not specified; it is initialized within the declaration `declare global{}`

In my TypeScript code, I'm facing a challenge when trying to add a global scope variable. In JavaScript (NodeJS), this process is straightforward: // index.js globalThis.helloWorld = 'Hello World!'; require('./log.js') // log.js c ...

Nuxt js is throwing an error stating that it is unable to locate the pages directory

I have made changes to the folder structure of my Nuxt.js project. I am encountering an issue: Error - Couldn't find a pages directory in D:\sample. How can I access the pages? .nuxt, app, node_modules, server, .eslintrc, package, package-lock ...

Using `it` with accessing class members

When testing whether a specific object/class is correctly wired up, I often utilize it.each to prevent writing repetitive tests. The issue arises when the type of the object doesn't have an index signature, requiring me to cast it to any for it to fun ...

How come the type checker is not throwing an error for this indexable type?

I recently delved into the Microsoft Typescript Handbook and found myself intrigued by the indexable types chapter. To gain a deeper understanding, I decided to experiment with the code provided. Strangely enough, upon running this particular piece of code ...

"Enhance your web development with TypeScript and react-select

I find myself in a peculiar predicament. Currently, I am immersing myself in learning TypeScript and here is a brief overview of what transpired so far: const [selectedBankCode , setSelectedBankCode] = useState<string | undefined>(); const [selecte ...

How can I assign two different colors based on the type in Typescript?

I am configuring a color property based on the nature of the display. colorStyle: { textAlign: "center", backgroundColor: "transparent", color: (theme.colors.BaseColor.Red as any).Red4, } The cu ...

Fetch routes from an external API using a component and integrate them seamlessly into the router

I want to fetch my routes from an external API, considering that some users may not have the necessary permissions to access a particular module. My navbar makes an API request to retrieve all available modules. These modules contain the file path for the ...

If I do not utilize v-model within computed, then computed will not provide a value

I'm fairly new to coding in JS and Vue.js. I've been attempting to create a dynamic search input feature that filters an array of objects fetched from my API based on user input. The strange issue I'm coming across is that the computed metho ...

Does the method in the superclass "not exist" within this type....?

Our TS application utilizes a JavaScript library, for which we crafted a .d.ts file to integrate it with TypeScript. Initially, the file resided in a "typings" directory within the project and everything operated smoothly. Subsequently, we opted to relocat ...

Vue event manager, accessible for all components

I have created a new Vue instance and assigned it to the window object, thinking that it would be accessible throughout all components. I expected this setup to allow me access to events emitted anywhere within my application. However, it seems like this ...

A new feature introduced in TypeScript, expression-level syntax was not present until it was added in JavaScript

Celebrating a Decade of TypeScript remarked that "It’s quite remarkable how the design goals set for TypeScript have stood the test of time." I am particularly intrigued by the goal of "Avoid adding expression-level syntax." One user even brought up thi ...

After modifying environment variables in Vue.js, the application still refers to the previous values

Currently, I am working on a Vue.js project where I have a .env.development file with various VUE_APP_* environment variables. Despite changing the values of some variables, the Vue.js code continues to reference the previous values. I have attempted mult ...

The search is on for the elusive object that Angular 2/4

Why am I encountering the error message saying "Cannot find name 'object'"? The error message is: Error: core.service.ts (19,23): Cannot find name 'object'. This error occurs in the following line of code: userChange: Subject<ob ...

Unable to make a POST request using axios

I'm having trouble populating a table using vue.js and axios in my visual studio project. Every time I run the solution, I see an empty table with just the heading Title. I experimented with another POST request method but unfortunately, it didn&apos ...

Attempting to perform an API invocation on a distant endpoint utilizing NestJS

var unirest = require("unirest"); var req = unirest("GET", "https://edamam-edamam-nutrition-analysis.p.rapidapi.com/api/nutrition-data"); req.query({ "ingr": "1 large apple" }); req.headers({ &qu ...

Ways to initiate a fresh API request while utilizing httpClient and shareReplay

I have implemented a configuration to share the replay of my httpClient request among multiple components. Here is the setup: apicaller.service.ts import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http& ...

Can Vue 3 be utilized with both the composition API and vue class components?

For the past 8 months, our project has been developed using Vue 3 and the class components. However, it appears that the class components are no longer being maintained. Therefore, we have decided to gradually transition to the composition API, specificall ...