Ionic Framework VueJS and Dynamic Slides Using SwiperJS

Ionic Framework VueJS and SwiperJS

Ionic has depreciated IonSlides in the v6 release and recommends using the underlying library SwiperJS.

In this short post I show how to integrate SwiperJS and it VueJS wrapper to display a set of slides holding IonCards. How to dynamically add and removed slides from the list of slides using the SwiperJS library.

See Previous Video For Setting Up Vite & Ionic Project

Also because vitejs is awesome and fast, I will be using that as the starting point for my application. See more information on Ionic and ViteJS and how to setup the app in the video I posted last week.

Video Version of Blog Post

Adding SwiperJS

after the project is set up you need to add SwiperJS to the project

npm i swiper

Also we will be adding some styling from the swiper library so to avoid the sass errors, we need to install another library

npm install -D sass

Creating Slides Page

This is the template section for the component, most of this is basic ionic setup

<template>
  <ion-page>
    <ion-header>
      <ion-toolbar>
        <ion-buttons slot="start">
          <ion-back-button></ion-back-button>
        </ion-buttons>
        <ion-title>Ionic Vite - Swiper - Slides</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content>

    // will add slides content here...

    </ion-content>
  </ion-page>
</template>

Next we need to include the appropriate imports

<script lang="ts">
import { defineComponent, ref } from "vue";
import {
  IonContent,
  IonButton,
  IonButtons,
  IonBackButton,
  IonPage,
  IonHeader,
  IonTitle,
  IonToolbar,
  IonModal,
  IonCard,
  IonCardContent,
} from "@ionic/vue";

// Import Swiper Vue.js components
import { Swiper, SwiperSlide } from "swiper/vue";

// Import Swiper styles
import "swiper/swiper.scss";

export default defineComponent({
  name: "Slides",
  components: {
    IonHeader,
    IonPage,
    IonButton,
    IonButtons,
    IonBackButton,
    IonContent,
    IonTitle,
    IonToolbar,
    IonCard,
    IonCardContent,
    Swiper,
    SwiperSlide,
  },
  setup() {
    return {
    };
  },
});
</script>

There are some styles that i figured out to make the page look ok, you can play around with them in your example. The .swiper-slide and .swiper-container are classes from the SwiperJS documentation

<style>
ion-card {
  height: 90%;
}
.swiper-slide {
  padding: 12px;
  text-align: center;
  font-size: 28px;
  background: #fff;
}
.swiper-container {
  width: 100%;
  height: 80%;
}
</style>

Lets create a data structure, slideData to hold our slide content and be sure to return it so it can be accessed in the template

setup() {

    // data used to draw slides
    const slideData = ref<any>([{ id: "1" }]);

    return {
      slideData,
    };
  },

Dynamically Adding Slides To Page

Lets add our slide components to the template, we will loop through the slideData to render a slide for each entry in the slideData

<template>
  <ion-page>
    <ion-header>
      <ion-toolbar>
        <ion-title>Ionic Vite - Swiper - Slides</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content>
      <!-- header on page -->
      <div style="padding-left: 10px">
        <h1>Swiperjs Slides</h1>
      </div>

      <!-- slides created by loopong through slideData -->
      <swiper :pagination="{ clickable: true }" >
        <template v-for="s in slideData" :key="s.id">
          <swiper-slide>
            <!-- put IonCard inside of the swiper-slide element -->
            <ion-card>
              <ion-card-content>
                <!-- using name from data for slide title -->
                <div>Slide Name {{ s.name }}</div>
              </ion-card-content>
            </ion-card>
          </swiper-slide>
        </template>
      </swiper>
    </ion-content>
  </ion-page>
</template>

One last step is making sure that the swiper library knows that we have updated the DOM by adding slides. The documentation states that when adding slides through javascript, you should call swiper.update() we will do that in the onMounted lifecycle event.

onMounted(() => {
    setTimeout(() => {
      swiperRef.value.update();
    }, 1);
  });

Now to add more slides to the page, we just need to add more data to the slideData object and since it is a ref, and observable, when it changes the template will be re-rendered with the new content.

Add a new function in setup and return the function so it can be accessed in the template

setup() {
  // data used to draw slides
  const slideData = ref<any>([{ id: "1", name: "initial slide" }]);

  // adds a slide to the list by updating the data that
  // the slides are derived from
  const addSlide = () => {
    slideData.value = [
      { id: new Date().getTime() + "", name: new Date() + "" },
      ...slideData.value,
    ];
  };

  return {
    slideData,
    addSlide,
  };
},

Add a new button in the templated

<template>
  <ion-page>
    <ion-header>
      <ion-toolbar>
        <ion-title>Ionic Vite - Swiper - Slides</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content>
      <!-- header on page -->
      <div style="padding-left: 10px">
        <h1>Swiperjs Slides</h1>
      </div>

      <!-- slides created by loopong through slideData -->
      <swiper :pagination="{ clickable: true }">
        <template v-for="s in slideData" :key="s.id">
          <swiper-slide>
            <!-- put IonCard inside of the swiper-slide element -->
            <ion-card>
              <ion-card-content>
                <!-- using name from data for slide title -->
                <div>Slide Name {{ s.name }}</div>
              </ion-card-content>
            </ion-card>
          </swiper-slide>
        </template>
      </swiper>

      <!--------------------------------->
      <!-- new button for adding slide -->
      **<div style="padding-left: 10px">
        <ion-button @click="addSlide">ADD SLIDE</ion-button>
      </div>**

    </ion-content>
  </ion-page>
</template>

Getting The Swiper Instance

The SwiperJS Vue Component has an event that will provide us with the swiper instance as soon as it is available. Lets change the template and the setup function to get the instance

@swiper="onSwiperInstance" listener added to get swiper instance

<!-- slides created by loopong through slideData -->
<!-- @swiper="onSwiperInstance" listener added to get swiper instance -->
<swiper :pagination="{ clickable: true }"  **@swiper="onSwiperInstance"**>
  <template v-for="s in slideData" :key="s.id">
    <swiper-slide>
      <!-- put IonCard inside of the swiper-slide element -->
      <ion-card>
        <ion-card-content>
          <!-- using name from data for slide title -->
          <div>Slide Name {{ s.name }}</div>
        </ion-card-content>
      </ion-card>
    </swiper-slide>
  </template>
</swiper>

need to change setup to include the function onSwiperInstance and return it so the template can access it

setup() {
  // data used to draw slides
  const slideData = ref<any>([{ id: "1", name: "initial slide" }]);

  **// NEW -> 
  // reference to swiper
  const swiperRef = ref<any>(null);**

  onMounted(() => {
    setTimeout(() => {
      swiperRef.value.update();
    }, 1);
  });

  **// NEW -> 
  // used to get an instance of the swiper object after it is
  // initialized
  const onSwiperInstance = (v: any) => {
    console.log("onSwiperInstance", v);
    swiperRef.value = v;
  };**

  // adds a slide to the list by updating the data that
  // the slides are derived from
  const addSlide = () => {
    slideData.value = [
      { id: new Date().getTime() + "", name: new Date() + "" },
      ...slideData.value,
    ];
  };

  return {
    slideData,
    addSlide,
    **onSwiperInstance**
  };
},

If you look in the console, you will see that we now have the swiper instance to be utilized to call any of the functions available to you from the SwiperJS API

Removing Slides from Page

We remove slides the same way we add slides, by updating the slideData property of the component.

Lets add a delete button to the slide, the button will call a function removeSlide and as a parameter, pass in the id of the slide to remove.

<swiper :pagination="{ clickable: true }"  **@swiper="onSwiperInstance"**>
  <template v-for="s in slideData" :key="s.id">
    <swiper-slide>
      <!-- put IonCard inside of the swiper-slide element -->
      <ion-card>
        <ion-card-content>
          <!-- using name from data for slide title -->
          <div>Slide Name {{ s.name }}</div>
          <ion-button @click="removeSlide(s.id)">DELETE SLIDE</ion-button>
        </ion-card-content>
      </ion-card>
    </swiper-slide>
  </template>
</swiper>

Lets update to setup to include the removeSlide function

setup() {
  // data used to draw slides
  const slideData = ref<any>([{ id: "1", name: "initial slide" }]);

  // reference to swiper
  const swiperRef = ref<any>(null);

  onMounted(() => {
    setTimeout(() => {
      swiperRef.value.update();
    }, 1);
  });

  // used to get an instance of the swiper object after it is
  // initialized
  const onSwiperInstance = (v: any) => {
    console.log("onSwiperInstance", v);
    swiperRef.value = v;
  };

  // adds a slide to the list by updating the data that
  // the slides are derived from
  const addSlide = () => {
    slideData.value = [
      { id: new Date().getTime() + "", name: new Date() + "" },
      ...slideData.value,
    ];
  };

  // remove a slide from the list by filtering the data that
  // the slides are derived from
  const removeSlide = (slideId: any) => {
    let newData = slideData.value.filter((s: any) => s.id !== slideId);
    slideData.value = newData;
  };

  return {
    slideData,
    addSlide,
    onSwiperInstance,
    removeSlide
  };
},

FULL SOURCE CODE

<template>
  <ion-page>
    <ion-header>
      <ion-toolbar>
        <ion-title>Ionic Vite - Swiper - Slides</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content>
      <div style="padding-left: 10px">
        <h1>Swiperjs Slides</h1>
      </div>
      <swiper :pagination="{ clickable: true }" @swiper="onSwiperInstance">
        <template v-for="s in slideData" :key="s.id">
          <swiper-slide>
            <ion-card>
              <ion-card-content>
                <div>Slide {{ s.id }}</div>
                <ion-button @click="removeSlide(s.id)">DELETE SLIDE</ion-button>
              </ion-card-content>
            </ion-card>
          </swiper-slide>
        </template>
      </swiper>
      <div style="padding-left: 10px">
        <ion-button @click="addSlide">ADD SLIDE</ion-button>
      </div>
    </ion-content>
  </ion-page>
</template>

<script lang="ts">
import { defineComponent, ref } from "vue";
import {
  IonContent,
  IonButton,
  IonPage,
  IonHeader,
  IonTitle,
  IonToolbar,
  IonCard,
  IonCardContent,
} from "@ionic/vue";
import SwiperCore, { Navigation, Pagination, Scrollbar, A11y } from "swiper";

// Import Swiper Vue.js components
import { Swiper, SwiperSlide } from "swiper/vue";

// Import Swiper styles
import "swiper/swiper.scss";
import "swiper/components/navigation/navigation.scss";
import "swiper/components/pagination/pagination.scss";
import "swiper/components/scrollbar/scrollbar.scss";

export default defineComponent({
  name: "Slides",
  components: {
    IonHeader,
    IonPage,
    IonButton,
    IonContent,
    IonTitle,
    IonToolbar,
    IonCard,
    IonCardContent,
    Swiper,
    SwiperSlide,
  },
  setup() {
    // reference to swiper
    const swiperRef = ref<any>(null);

    // data used to draw slides
    const slideData = ref<any>([{ id: "1" }]);

    onMounted(() => {
      setTimeout(() => {
        swiperRef.value.update();
      }, 1);
    });

    // used to get an instance of the swiper object after it is
    // initialized
    const onSwiperInstance = (v: any) => {
      console.log("onSwiperInstance", v);
      swiperRef.value = v;
    };

    // adds a slide to the list by updating the data that
    // the slides are derived from
    const addSlide = () => {
      slideData.value = [{ id: new Date().getTime() + "" }, ...slideData.value];
    };

    // remove a slide from the list by filtering the data that
    // the slides are derived from
    const removeSlide = (slideId: any) => {
      let newData = slideData.value.filter((s: any) => s.id !== slideId);
      slideData.value = newData;
    };

    return {
      slideData,
      onSwiperInstance,
      addSlide,
      removeSlide,
    };
  },
});
</script>

<style>
ion-card {
  height: 90%;
}
.swiper-slide {
  padding: 12px;
  text-align: center;
  font-size: 28px;
  background: #fff;
}
.swiper-container {
  width: 100%;
  height: 80%;
}
</style>

Github Project

GitHub logo aaronksaunders / vite-ionic-slides-project

ionic v6 beta vitejs slides implemented using swiperjs

Getting Started with ViteJS, Ionic Framework Beta v6 SwiperJS And VueJS

This template should help get you started developing with Ionic, Vue 3 and Typescript in Vite.

Ionic has depreciated IonSlides in the v6 release and recommends using the underlying library SwiperJS.

In this short post I show how to integrate SwiperJS and it VueJS wrapper to display a set of slides holding IonCards. How to dynamically add and removed slides from the list of slides using the SwiperJS library.

Recommended IDE Setup

VSCode + Vetur. Make sure to enable vetur.experimental.templateInterpolationService in settings!

If Using <script setup>

<script setup> is a feature that is currently in RFC stage. To get proper IDE support for the syntax, use Volar instead of Vetur (and disable Vetur).

Type Support For .vue Imports in TS

Since TypeScript cannot handle type information for .vue imports, they are shimmed to be a generic Vue…

28