Ionic Framework VueJS and Dynamic Slides Using SwiperJS
Tags: swiperjs, ionic, vue, vite
Published: 2021-07-18 23:31:42

## Ionic Framework VueJS and SwiperJS

Ionic has depreciated IonSlides in the v6 release and recommends using the underlying library [SwiperJS](https://swiperjs.com/).

In this short post I show how to integrate [SwiperJS and it VueJS](https://swiperjs.com/vue) 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.

- [https://youtu.be/JnfAH1gXVdU](https://youtu.be/JnfAH1gXVdU)

{% youtube JnfAH1gXVdU %}

### Video Version of Blog Post
{% youtube pyqHuJSAgeY %}

### Adding SwiperJS

after the project is set up you need to add [SwiperJS](https://swiperjs.com/) to the project

```jsx
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

```jsx
npm install -D sass
```

### Creating Slides Page

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

```jsx
<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

```jsx
<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

```jsx
<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

```jsx
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`

```jsx
<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.

```jsx
  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

```jsx
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

```jsx
<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

- [https://swiperjs.com/vue#swiper-events](https://swiperjs.com/vue#swiper-events)
- [https://swiperjs.com/vue#swiper-props](https://swiperjs.com/vue#swiper-props)

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

```jsx
<!-- 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

```jsx
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.

```jsx
<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

```jsx
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

```jsx
<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 aaronksaunders/vite-ionic-slides-project %}