"ion-item-sliding" sliding does not work when "ion-tabs" are rendered inside "ion-app"

So, to elaborate, Please find below the code I have done so far:

App.vue:

<template>
  <ion-app>
    <ion-router-outlet />
  </ion-app>
  <!--  <main-tabs />-->
</template>

<script>
  // import MainTabs from '@/components/base/MainTabs'
  import { defineComponent } from 'vue'

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

BaseLayout.vue:

<template>
  <ion-page>
    <slot name="menu" />
    <ion-header>
      <ion-toolbar id="main-toolbar">
        <ion-buttons slot="start">
          <slot name="toolbar-start-button" />
        </ion-buttons>
        <ion-title>{{ pageTitle }}</ion-title>
        <ion-buttons slot="end">
          <slot name="toolbar-end-button" />
        </ion-buttons>
      </ion-toolbar>
    </ion-header>
    <ion-content :id="content_id" color="tertiary" fullscreen>
      <slot />
    </ion-content>
  </ion-page>
  <!--  <main-tabs />-->
</template>

<script>
  // import MainTabs from '@/components/base/MainTabs'
  export default {
    name: 'BaseLayout',
    components: {
      // MainTabs,
    },
    props: {
      content_id: {
        type: String,
        required: false,
        default: '',
      },
      pageTitle: {
        type: String,
        required: true,
      },
    },
  }
</script>

<style></style>

MainTabs.vue:

<template>
  <ion-tabs>
    <ion-tab-bar slot="bottom" color="tertiary">
      <ion-tab-button tab="budgets" href="/budgets">
        <ion-icon
          :src="require(`@/static/icons/calculator.svg`)"
          color="primary"
        />
        <ion-label color="primary">Budgets</ion-label>
      </ion-tab-button>
      <ion-tab-button tab="categories" href="/categories">
        <ion-icon
          :src="require(`@/static/icons/categories.svg`)"
          color="primary"
        />
        <ion-label color="primary">Categories</ion-label>
      </ion-tab-button>
    </ion-tab-bar>
  </ion-tabs>
</template>

<script>
  export default {
    name: 'MainTabs.vue',
  }
</script>

<style scoped></style>

CategoryList.vue:

<template>
  <ion-list class="ion-no-padding" ref="categoryListRef">
    <category-list-item
      v-for="category in categories"
      :key="category.category_id"
      :category="category"
      @onEdit="resetSlides"
      @onDelete="onDelete"
    />
  </ion-list>
</template>

<script>
  import { ref } from 'vue'
  import CategoryListItem from '@/components/categories/CategoryListItem.vue'
  export default {
    name: 'CategoryList',
    props: {
      categories: {
        type: Array,
        required: true,
      },
    },
    components: {
      CategoryListItem,
    },
    setup(_, { emit }) {
      const categoryListRef = ref(null)
      const resetSlides = async () => {
        await categoryListRef.value.$el.closeSlidingItems()
      }

      const onDelete = (category) => {
        emit('onDelete', category)
      }

      return {
        categoryListRef,
        resetSlides,
        onDelete,
      }
    },
  }
</script>

<style></style>

CategoryListItem.vue:

<template>
  <ion-item-sliding class="slide-item">
    <ion-item-options side="start">
      <ion-item-option color="danger" @click="deleteCategory(category)">
        <ion-icon :icon="trashOutline" slot="icon-only" />
      </ion-item-option>
    </ion-item-options>
    <ion-item lines="full" color="secondary">
      <ion-icon :src="require(`@/static/icons/categories.svg`)" slot="start" />
      <ion-label>{{ category.name }}</ion-label>
    </ion-item>
    <ion-item-options side="end">
      <ion-item-option @click="editCategory(category.category_id)">
        <ion-icon :src="require(`@/static/icons/edit.svg`)" slot="icon-only" />
      </ion-item-option>
    </ion-item-options>
  </ion-item-sliding>
</template>

<script>
  import { useRouter } from 'vue-router'
  import { trashOutline, create } from 'ionicons/icons'
  export default {
    name: 'CategoryListItem',
    props: {
      category: {
        type: Object,
        required: true,
      },
    },
    setup(_, { emit }) {
      const router = useRouter()

      const editCategory = async (categoryId) => {
        await router.push(`/categories/edit/${categoryId}`)
        await emit('onEdit')
      }

      const deleteCategory = async (category) => {
        emit('onDelete', category)
      }

      return {
        trashOutline,
        create,
        editCategory,
        deleteCategory,
      }
    },
  }
</script>

<style scoped></style>

and finally, CategoriesPage.vue (route (/categories) component):

<template>
  <base-layout pageTitle="Expense Categories">
    <template v-slot:toolbar-end-button>
      <ion-button router-link="/categories/add">
        <ion-icon slot="icon-only" :icon="add"></ion-icon>
      </ion-button>
    </template>
    <category-list
      v-if="categories.length"
      :categories="categories"
      @onDelete="deleteCategory"
    />
    <empty-list v-else />
    <main-tabs />
  </base-layout>
</template>

<script>
  import { computed } from 'vue'
  import { useStore } from 'vuex'
  import { add, arrowBack } from 'ionicons/icons'
  import actionTypes from '@/store/actionTypes'
  import CategoryList from '@/components/categories/CategoryList.vue'
  import EmptyList from '@/components/miscellaneous/EmptyList.vue'
  import MainTabs from '@/components/base/MainTabs.vue'
  export default {
    name: 'CategoriesPage',
    components: {
      CategoryList,
      EmptyList,
      MainTabs,
    },
    setup() {
      const store = useStore()

      const deleteCategory = (category) => {
        store.dispatch(actionTypes.REMOVE_CATEGORY, category)
      }

      return {
        add,
        arrowBack,
        categories: computed(() => store.getters.expense_categories),
        deleteCategory,
      }
    },
    ionViewDidEnter() {
      // this.$store.dispatch(actionTypes.FETCH_CATEGORIES).then(() => {
      //   console.log(this.categories)
      // })
    },
  }
</script>

<style scoped></style>

In CategoriesPage.vue, the category-list uses category-list-item which in turn uses ion-item-sliding component and the main-tabs component uses ion-tabs component.

When I place main-tabs inside base-layout (ion-page), the sliding on ion-item-sliding does not work. When I move ion-tabs outside ion-app in App.vue, everything works fine.

Now, since the documentation has examples where ion-tabs component is inside the ion-page, I would like to know what is really going wrong here.

Can you reproduce this in an Ionic starter app and provide a link to the GitHub repo?

@ldebeasi Well I am trying to do the same in the codesandbox, but the basic components are not getting rendered in their view, Can you have a look?

@ldebeasi I got the issue, I did not add the children routes to the tab structure as done in the starter tabs template…

1 Like