Subscribe on changes!

[vue-compat] @click not working on components

avatar
Jun 24th 2021

Version

3.1.2

Reproduction link

https://github.com/liamdebeasi/vue3-repro

Steps to reproduce

  1. Clone repo and run npm install.
  2. Run npm run serve to start up a dev server.
  3. Click the "Click me!" text and observe that nothing is logged to your Dev Tools Console.
  4. Quit the dev server and comment out the config.resolve.alias line in vue.config.js to disable the compat mode.
  5. Run the dev server again.
  6. Click the "Click me!" text and observe that this time the "Component was clicked!" text is logged to your Dev Tools Console.

What is expected?

I would expect that the click handler is fired regardless of whether or not I am using the compat build.

What is actually happening?

The click handler is only firing when not using the compat build.

avatar
Jun 24th 2021

In Vue 2 you need to use the .native modifier with v-on bindings when the component itself is not emitting click. I believe the compat build is aimed to align with this behavior.

avatar
Jun 24th 2021

Thanks! I tried that, but the click handler still does not fire. I am doing:

<app-cmp v-on:click.native="onClick()">Click me!</app-cmp>
avatar
Jun 24th 2021

The syntax you are using for the functional component seems off. It should be:

import { defineComponent, h } from 'vue';
const defineContainer = (name) => {
  const Container = ((props, { slots }) => {
      return h(name, props, slots.default && slots.default());
  });
  
  Container.displayName = name;
  return Container;
};

(https://v3.vuejs.org/guide/migration/functional-components.html#_2-x-syntax)

avatar
Jun 24th 2021

You have other warnings telling you the next steps for migration in the console, so I'm closing this.

Remember to use the forum or the Discord chat to ask questions!

avatar
Jun 24th 2021

Please correct me if I am wrong, but I thought my original syntax is also valid? Passing a function to defineComponent is the same thing as passing a setup function according to https://v3.vuejs.org/api/global-api.html#definecomponent. This particular syntax seems to be used in Vue 3 tests implying that it is valid as well: https://github.com/vuejs/vue-next/blob/349eb0f0ad78f9cb491278eb4c7f9fe0c2e78b79/packages/server-renderer/__tests__/render.spec.ts#L100-L103

Also even when using the syntax you proposed and after making the migrations suggested in the console, the issue persists.

avatar
Jun 24th 2021

.native doesn't work in the compat view, so there doesn't even seem to be a workaround for this. There's not steps shown in the console at all if not using .native - just nothing fires.

FYI this breaks all of the ionic/vue system with 3.1

avatar
Jun 30th 2021

@posva Any tips how to get click events to fire in components we don't control with 3.1 compat? I.e. via the ionic framework. Since the removal of .native there doesn't seem to be any way to make this work. There's no console migration notices besides to remove .native - just no click events fire. I think maybe this is a bug and 3.1 should still allow the use of .native handler when the combability hasn't disabled it?

avatar
Jun 30th 2021

.native is listed as being fully supported in compat build.

@liamdebeasi Your app is missing is the second part of the webpack config described in the migration guide - setting the compiler to compat mode:

config.module
      .rule('vue')
      .use('vue-loader')
      .tap(options => {
        return {
          ...options,
          compilerOptions: {
            compatConfig: {
              MODE: 2
            }
          }
        }
      })

After I added this to your repro, click worked.

@inspire22 .native is fully supported by the migration build. If you found a scenario where it doesn't work as intended, please open a new issue with a reproduction so we can check it out.

avatar
Jun 30th 2021

Thanks! I added the webpack config and changed the click handler to v-on:click.native and now the click events are working as expected. I think I got confused by the The migration build runs in Vue 2 mode by default line in the docs and thought that the webpack part was not necessary unless you needed to customize some part of the compat build.

edit: It might be good to revise the wording in the docs for this. I understood The migration build runs in Vue 2 mode by default as "when you turn the migration build on, it will run in Vue 2 mode without having to explicitly tell it to". But since you need to set compatConfig: { MODE: 2 } you really do need to explicitly tell it to run in Vue 2 mode. This led me to think that the Webpack config was not required. Thanks again for the help!

avatar
Jun 30th 2021

in step 3 of the Installation instructions it explicitly states:

In the build setup, alias vue to @vue/compat and enable compat mode via Vue compiler options.

Should that be changed to be clearer? Or should the introduction further up which threw you off be changed? If so, do you have something in mind? The folks in the docs-next repo would be happy to get suggestions.

avatar
Jun 30th 2021

Yeah I think the intro section threw me off with the way it was worded. I'll think of a clearer way to word it and make an issue/PR on the docs-next repo. Thanks!

avatar
Jun 30th 2021

@liamdebeasi I've made the .native updates to my ionic-based repo and click is still not fired - could this be related to the changed defineComponent vs what vue/ionic does? https://github.com/inspire22/ionic-click-bug

I'd been confused in thinking that .native wasn't supported because the included .eslint.rc had "plugin:vue/vue3-essential" which refuse to compile while .native was included.

Also - in ionic with vue version 3.0 .native wasn't required, correct? I guess this is a tricky interaction between the compat mode for vue and ionic's support for vue 3.0

avatar
Jun 30th 2021

Let's move the conversation back to the Ionic Framework repo and we can continue to discuss there.

avatar
Jun 30th 2021

@inspire22 The compiler config is not the solution in terms of Ionic.

Ionic exports Vue 3 components. But in compat mode, the app would treat them like Vue 2 components by default. You can do the following:

import {IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonButton} from "@ionic/vue";
import {defineComponent} from "vue";

IonButton.compatConfig = {
    MODE: 3
}

Which makes the alert pop up for me - without needing to add .native.

To make this less messy, you might have to come up with a clever wrapper or sth. Idea:

import {IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonButton} from "@ionic/vue";
import {defineComponent} from "vue";


// you would of course import this from a module in each of your components
function fixCompMode(components) {
    Object.entries(components).map(([name, component]) => {
        if (!component.compatConfig && name.startsWith('Ion')) {
            component.compatConfig = { MODE: 3 }
        }
    })
}

export default defineComponent({
    name: "Home",
    components: fixCompMode({
        IonContent,
        IonHeader,
        IonPage,
        IonTitle,
        IonToolbar,
        IonButton,
    }),
    methods: {

//...

This will add a very small one-time runtime overhead, but should not be harmful considering Vue 3 itself is faster than Vue 2.

avatar
Jul 1st 2021

Brilliant, thanks so much to everyone for their help!