Menu item is not clickable

Hi folks, I started with ionic some days ago and I think it is amazing, but now I am facing an error difficult to debug. The issue is that a menu item is not clickable and I don’t know why.

Here is my code:

Home.js

<template>
  <ion-page>
    <ion-menu side="start" menu-id="first" content-id="main">
      <ion-header>
        <ion-toolbar color="primary">
          <ion-title>Menú</ion-title>
        </ion-toolbar>
      </ion-header>
      <ion-content id="main">
        <ion-list>
          <ion-item @click="play">
            Jugar Trivia
          </ion-item>
          <ion-item>Menu Item</ion-item>
          <ion-item>Menu Item</ion-item>
          <ion-item>Menu Item</ion-item>
          <ion-item>Menu Item</ion-item>
        </ion-list>
      </ion-content>
    </ion-menu>
    ...
</template>

App.js

<template>
  <ion-app>
    <ion-router-outlet/>
  </ion-app>
</template>

<script>
import {IonApp} from '@ionic/vue';
import {IonRouterOutlet} from '@ionic/vue';
import {defineComponent} from 'vue';

export default defineComponent({
  name: 'App',
  components: {
    IonApp,
    IonRouterOutlet,
  }
});
</script>

When I click the first option with @click=“play” nothing happens, that method is working as expected. No error is being thrown, maybe I am missing something. Hope you can enlighten me!

Where is the play method in this code?

It is a simple redirection used in another button, here you have the code:

play() {
      console.log('clicked');
      this.router.push('/custom');
    },

there is no concept of this when using composition, ie setup… please show the complete SFC associated with home.js so we can help

Given I defined “play” as a method in the component I can use the “this” way.

This is the whole component:

<template>
  <ion-page>
    <ion-menu side="start" menu-id="first" content-id="main">
      <ion-header>
        <ion-toolbar color="primary">
          <ion-title>Menú</ion-title>
        </ion-toolbar>
      </ion-header>
      <ion-content id="main">
        <ion-list>
          <ion-item button @click="play">
            Jugar Trivia
          </ion-item>
          <ion-item>Menu Item</ion-item>
          <ion-item>Menu Item</ion-item>
          <ion-item>Menu Item</ion-item>
          <ion-item>Menu Item</ion-item>
        </ion-list>
      </ion-content>
    </ion-menu>

    <ion-header :translucent="true">
      <ion-toolbar>
        <ion-buttons slot="start">
          <ion-menu-button></ion-menu-button>
        </ion-buttons>
        <ion-title>Gurú del Anime</ion-title>
      </ion-toolbar>
    </ion-header>

    <ion-content :fullscreen="true">
      <!--<ion-header collapse="condense">
        <ion-toolbar>
          <ion-title size="large">Blank</ion-title>
        </ion-toolbar>
      </ion-header>-->

      <div id="container">
        <strong style="display: block">Bienvenido</strong>
        <br>
        <ion-button color="success" @click="play">Jugar Trivia</ion-button>

        <button class="button--primary">Button</button>
      </div>

      <!--<ion-fab vertical="bottom" horizontal="end" slot="fixed">
        <ion-fab-button>
          <ion-icon :icon="add"></ion-icon>
        </ion-fab-button>
      </ion-fab>-->
    </ion-content>
  </ion-page>
</template>

<script>
/* eslint-disable vue/no-unused-components */
/*eslint-disable no-unused-vars*/
import {AdOptions, AdSize, AdPosition} from 'capacitor-admob';
import {Plugins} from '@capacitor/core';
import {add} from 'ionicons/icons';
import {
  IonPage, IonContent, IonHeader,
  IonTitle, IonToolbar,
  IonFab, IonFabButton,
  IonIcon,
  IonButton,
  IonMenu, IonList, IonItem, menuController,
  IonButtons, IonMenuButton,
} from '@ionic/vue';
import {useRouter} from 'vue-router';
import {defineComponent} from 'vue';

const {Storage, AdMob, Toast} = Plugins;

export default defineComponent({
  name: 'Home',
  components: {
    IonPage, IonContent, IonHeader,
    IonTitle, IonToolbar,
    IonFab, IonFabButton,
    IonIcon,
    IonButton,
    IonMenu, IonList, IonItem,
    IonButtons, IonMenuButton,
  },
  setup() {
    return {
      router: useRouter(),
      add,
    }
  },
  async mounted() {
    //AdMob.initialize('ca-app-pub-3940256099942544~3347511713');
    setTimeout(async () => {
      //this.showBanner();
    }, 100);
    await menuController.enable(true, 'first');
  },
  methods: {
    async setItem() {
      await Storage.set({
        key: 'name',
        value: 'Hiram'
      });
    },
    async getItem() {
      const {value} = await Storage.get({key: 'name'});
      console.log('Got item: ', value);
    },
    showBanner() {
      const options = {
        adId: 'ca-app-pub-3940256099942544/6300978111',
        adSize: AdSize.SMART_BANNER,
        position: AdPosition.BOTTOM_CENTER,
        hasTabBar: false,  // make it true if you have TabBar Layout.
        tabBarHeight: 56,  // you can assign custom margin in pixel default is 56,
        margin: '150',
      };

      AdMob.showBanner(options)
          .then(
              async (value) => {
                /*console.log(value);  // true
                await Toast.show({
                  text: 'Showing Banner AD.'
                });*/
              },
              async (error) => {
                /*await Toast.show({
                  text: 'NOT SHOWING Banner AD.'
                });
                console.log("ERROR");
                console.error(error); // show error*/
              }
          );

      AdMob.addListener('onAdLoaded', async (info) => {
        /*console.log(info);
        await Toast.show({
          text: 'ON AD LOADED EVENT'
        });*/
      });
    },
    play() {
      console.log('clicked');
      this.router.push('/custom');
    },
    /*openFirst() {
      menuController.enable(true, 'first');
      menuController.open('first');
    },*/
  },
});
</script>

I believe your problem comes from mixing composition api with options. You are trying to set useRouter in setup, then use it inside options. Composition api is great, but it does add confusion to things, and that is what you’ve run into here.

If you are writing code inside the setup function, then yes, use useRouter(), as in:

setup() {
  const router = userRouter();

  function play() {
    router.push('/somewhere');
  }
  
  return {
    play
  }
}

That would then actually work, without you needing to add play() to the methods section.

However, that’s not what you are doing, you are using the router in the traditional options api. To do that, you use this.$router.push('/custom');

When using the options api, the router is already made available to you on every component. When using composition api, there is no this, which is why useRouter() is required.

Try changing your code to:

play() {
  this.$router.push('/custom');
}

And see if that does it. It should. Alternatively, you could modify your setup method to add the play method there and return it, which would also work.

I tried the play function inside the setup suggestion and it works, but what is not working is the click on the item menu and I don’t know why.

The following is my template:

<template>
  <ion-page>
    <ion-menu side="start" menu-id="first" content-id="main">
      <ion-header>
        <ion-toolbar color="primary">
          <ion-title>Menú</ion-title>
        </ion-toolbar>
      </ion-header>
      <ion-content id="main">
        <ion-list>
          <ion-item button @click="play">
            Jugar Trivia
          </ion-item>
          <ion-item>Menu Item</ion-item>
          <ion-item>Menu Item</ion-item>
          <ion-item>Menu Item</ion-item>
          <ion-item>Menu Item</ion-item>
        </ion-list>
      </ion-content>
    </ion-menu>```

Any idea why the click is not being listened?

I suggest you start with the CLI/template for side menu? if you are still running into problems?

Could you point me out to the templates? I searched in docs but I couldn’t find it.

Aarons-iMac:vuevideos aaronksaunders$ ionic start

Pick a framework! 😁

Please select the JavaScript framework to use for your new app. To bypass this prompt next time, supply a value
for the --type option.

? Framework: Vue

Every great app needs a name! 😍

Please enter the full name of your app. You can change this at any time. To bypass this prompt next time, supply
name, the first argument to ionic start.

? Project name: side-menu

Let's pick the perfect starter template! 💪

Starter templates are ready-to-go Ionic apps that come packed with everything you need to build your app. To
bypass this prompt next time, supply template, the second argument to ionic start.

? Starter template: (Use arrow keys)
❯ tabs     | A starting project with a simple tabbed interface 
  sidemenu | A starting project with a side menu with navigation in the content area 
  blank    | A blank starter project 
  list     | A starting project with a list 
1 Like

Thanks for your suggestion!

I’ll import what I need from the template.

1 Like