[swiper]画像の切り替えを行う際、navigationボタンを押下した時に、表示されている画像のテキストをボタン押下ごとに切り替える

実際の現場で、`swiper`を使用して
画像の切り替えを行う際、navigationボタンを押下した時に、
表示されている画像のテキストも切り替える実装に
かなり苦戦したため、備忘録として記載します。

## 初めに
swiperとは何ぞやという人は、下記の公式ページを参照ください。
それぞれ目的の合ったリンクを確認するのが良いと思います。
Vueの場合：[swiper Vue.js Components](https://swiperjs.com/vue)
Nuxtの場合：[Swiper Nuxt Modules](https://nuxt.com/modules/swiper)

## 本題
ここでは、3つのコンポーネントを使用して、
子→親→子という形で、`defineProps`及び`defineEmits`を
行います。
まず全体のソースコードはこちらになります。
###### 親コンポーネント(index.vue)
/page/swiper/index.vue
```rb
<template>
  <div>
    <SwiperComponent @updateComment="updateComment" />
    <CommentDisplay :comment="currentComment"/>
  </div>
</template>
<script setup lang="ts">
import { ref } from 'vue';

const currentComment = ref<string>()

const updateComment = (newComment: string) => {
  currentComment.value = newComment
}
</script>
```
###### 子コンポーネント(SwiperComponent.vue)
/components/SwiperComponent.vue
```rb
<template>
  <swiper 
    :modules="[Navigation]" 
    :slides-per-view="1"
    @slideChange="onSlideChange"
    :navigation="{
      nextEl: '.swiper-button-next',
      prevEl: '.swiper-button-prev'
    }"
  >
    <swiper-slide v-for="image in images" :key="image.id">
      <div>
        <img :src="image.src" :alt="'Image ' + image.id" />
      </div>
    </swiper-slide>
  </swiper>
  <div class="swiper-button-next"></div>
  <div class="swiper-button-prev"></div>
</template>

<script setup lang="ts">
import 'swiper/css';
import 'swiper/css/navigation';
import { Swiper, SwiperSlide } from 'swiper/vue';
import { Navigation } from 'swiper/modules';
import { onMounted, ref } from 'vue'

interface SwiperInstance {
  realIndex: number;
}

//data
const images = [
  {
    id: 1,
    src: 'assets\img\カワイクテゴメンネ.jpg',
    comment: 'This is the first image.'
  },
  {
    id: 2,
    src: 'assets\img\image_processing20200630-1-1rg32vr.jpg',
    comment: 'This is the second image.'
  },
  {
    id: 3,
    src: 'assets\img\IMG_3490-1024x683.jpg',
    comment: 'This is the third image.'
  }
];

const emit = defineEmits(['updateComment'])

const onSlideChange = (swiper: SwiperInstance) => {
  const currentComment = images[swiper.realIndex].comment
  emit('updateComment', currentComment)
}

// 初期描画時のコメントを取得する
onMounted(() => {
  emit('updateComment', images[0].comment)
})
</script>

<style scoped>
img {
  width: 100%;
  height: auto;
}

p {
  text-align: center;
  margin: 10px 0 0;
}

.swiper-button-next,
.swiper-button-prev {
  color: #000;
  cursor: pointer;
}
</style>

```
###### 子コンポーネント(CommentDisplay.vue)
/components/CommentDisplay.vue
```rb
<template>
  <dev>
    <p>{{ props.comment }}</p>
  </dev>
</template>

<script setup lang="ts">
const props = defineProps<{
  comment: string | undefined
}>();
</script>

<style scoped>
p {
  text-align: center;
  margin: 10px 0 0;
}
</style>
```
##### 1\. 子コンポーネント(SwiperComponent.vue)について
子コンポーネントに`swiper`を実装しています
```rb
import 'swiper/css';
import 'swiper/css/navigation';
import { Swiper, SwiperSlide } from 'swiper/vue';
import { Navigation } from 'swiper/modules';
```
各swiperをインポートして、`'swiper/modules'`でナビゲーションのモジュールを設定する
```rb
<swiper 
    :modules="[Navigation]" 
    :slides-per-view="1"
    @slideChange="onSlideChange"
    :navigation="{
      nextEl: '.swiper-button-next',
      prevEl: '.swiper-button-prev'
    }"
  >
    <swiper-slide v-for="image in images" :key="image.id">
      <div>
        <img :src="image.src" :alt="'Image ' + image.id" />
      </div>
    </swiper-slide>
  </swiper>
  <div class="swiper-button-next"></div>
  <div class="swiper-button-prev"></div>
```
templateで実装する方法は上記の実装を行い、
`:modules="[Navigation]`で、ナビゲーションのモジュールを提供して
ナビゲーションボタンを使ってスライドを切り替える機能を提供しています。
`:modules`属性を設定して、使用するモジュールを指定し、ここでは`[Navigation]`を指定してナビゲーション機能を有効にしています。

`:slides-per-view="1"`は、一度表示されるスライドの数を指定します。
`1`と設定したことで、一度に1つのスライドが表示される設定になります。

`:navigation="{
      nextEl: '.swiper-button-next',
      prevEl: '.swiper-button-prev'
    }"`は、カスタムナビゲーションボタンを設定します。
`nextEl`と`prevEl`プロパティを使って、次へボタンと前へボタンの要素を指定します。
今回の場合、`.swiper-button-next`と`.swiper-button-prev`はそれぞれ「次へ」ボタンと「前へ」ボタンのCSSセレクタを指します。
これにより、カスタムのナビゲーションボタンが機能するようになります。
```rb
const emit = defineEmits(['updateComment'])

const onSlideChange = (swiper: SwiperInstance) => {
  const currentComment = images[swiper.realIndex].comment
  emit('updateComment', currentComment)
}

onMounted(() => {
  emit('updateComment', images[0].comment)
})
```
`onSlideChange`関数の引数に`swiper: SwiperInstance`を取得
`SwiperInstance`の型定義は以下の設定にしておく
※ここでtypescriptの型を定義する
```rb
interface SwiperInstance {
  realIndex: number;
}
```
以下のコードで、`swiper.realIndex`を使って現在のスライドのインデックスを取得し、`images`の配列から対応する`comment`を取得するようにする
```rb
const currentComment = images[swiper.realIndex].comment
```
最後に`emit`で親コンポーネントにcommentを送信する
```rb
emit('updateComment', currentComment)
```

##### 2\. 親コンポーネント(index.vue)について
`updateComment`でemitされたコメントを`newComment`に引数として持たせ、
`currentComment`の`ref`にコメントを格納する
```rb
const currentComment = ref<string>()

const updateComment = (newComment: string) => {
  currentComment.value = newComment
}
```
`<CommentDisplay :comment="currentComment"/>`で別のコンポーネントにpropsする
```rb
<CommentDisplay :comment="currentComment"/>
```

##### 3\. 子コンポーネント(CommentDisplay.vue)について
親コンポーネントから渡されたpropsを設定し、`<p>{{ props.comment }}</p>`でコメントを表示する
```rb
<script setup lang="ts">
const props = defineProps<{
  comment: string | undefined
}>();
</script>
```
## まとめ
ざっくりですが、swiperの仕組みが分かった経験でした
ほぼ実装コードはchatGPTに頼っての解決でしたが、chatGPTで的確な検索をするにはやはり知識を屈指して行うのが最短ルートだったなと感じました。
まだ分からないことも多々ありますが、自分自身で解決したことがとても嬉しいと感じたのが率直な感想です。
また、何かご指摘や誤った情報をしてしまっていましたらご指摘お願いいたします。。

公式の内容も確認しつつ行いましたが、ほぼchatGPTのおかげなので
今度は上手く調べられるようにしていきたいな、
(追伸. この実装を行うのに約3日くらいの時間を費やしたので今度はもう少し頑張りたい(´;ω;｀))