@chron You don’t need to manually add event listeners. Just import the Bootstrap JavaScript in the JavaScript file where you create your Vue application:
import 'bootstrap';
import { createApp } from 'vue';
const app = createApp({
//
});
app.mount('#app');
This will include and initialise the Bootstrap JavaScript components, including offcanvas.
For things like modals, I do tend to wrap them in Vue components, and wrap the Bootstrap events in Vue component events:
<script setup>
import { defineEmits, defineProps, onMounted, ref, watch } from 'vue';
import { Modal } from 'bootstrap';
const props = defineProps({
id: {
required: true,
type: String,
},
show: {
default: false,
required: false,
type: Boolean,
},
title: {
required: true,
type: String,
},
});
const emit = defineEmits(['hide', 'hidden', 'show', 'show']);
const bootstrapModal = ref(null);
const modalEl = ref(null);
onMounted(() => {
if (modalEl.value) {
bootstrapModal.value = Modal.getOrCreateInstance(modalEl.value);
modalEl.addEventListener('hide.bs.modal', () => emit('hide'));
modalEl.addEventListener('hidden.bs.modal', () => emit('hidden'));
modalEl.addEventListener('show.bs.modal', () => emit('show'));
modalEl.addEventListener('shown.bs.modal', () => emit('shown'));
}
});
watch(() => props.show, (show) => {
if (bootstrapModal.value) {
show ? bootstrapModal.value.show() : bootstrapModal.value.hide();
}
});
</script>
<template>
<div
aria-hidden="true"
class="fade modal"
ref="modalEl"
tabindex="-1"
v-bind:aria-labelledby="`${id}-label`"
v-bind:id="id"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" v-bind:id="`${id}-label`">{{ title }}</h1>
<button aria-label="Close" class="btn-close" data-bs-dismiss="modal" type="button"></button>
</div>
<div class="modal-body">
<slot name="body"></slot>
</div>
<div class="modal-footer" v-if="'footer' in $slots">
<slot name="footer"></slot>
</div>
</div>
</div>
</div>
</template>
This means you can then show and hide the modal with a show prop, and also receive events when the modal is closed in order to update any props in the parent component:
<script setup>
import { ref } from 'vue';
const modalOpen = ref(false);
</script>
<template>
<CustomModal v-bind:show="modalOpen" v-on:hide="modalOpen = false">
<template v-slot:body>
<!-- Modal body -->
</template>
</CustomModal>
</template>
You could probably re-factor this to a v-model directive if you really wanted to.