I've created a custom Flickity.vue object for Vue 3 and TypeScript support, following the accepted answer here.
When I try to listen for events on my flickity
carousel however, i'm plagued by runtime type errors in my console: this$refs.flickity.on is not a function
What's causing my current approach to break?
DisplayPage.vue
<template>
<div class="row">
<div class="col d-block m-auto">
<flickity ref="flickity" @init="onInit" :options="flickityOptions">
</flickity>
</div>
</div>
</template>
<script lang="ts">
import {defineComponent} from "vue";
import Flickity from "./widgets/Flickity.vue";
export default defineComponent({
components: {
Flickity
},
data() {
return {
flickityOptions: {
initialIndex: 1,
prevNextButtons: true,
pageDots: true,
wrapAround: false,
}
};
},
methods: {
configureBankAccountCarousel() {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.$refs.flickity as any).append(this.makeFlickityCell())
},
makeFlickityCell() {
const cell = document.createElement('div')
cell.className = 'carousel-cell'
cell.textContent = "Bank Acct"
}
},
mounted() {
this.configureBankAccountCarousel();
this.configureBankAccountCarousel();
this.$nextTick(() => {
// EVENTS
(this.$refs.flickity as any).on('ready', function () { //Type ERROR here
console.log('Flickity is ready!')
})
})
},
});
</script>
Flickity.vue
<template>
<div ref="root" class="flickity">
<slot />
</div>
</template>
<script lang="ts">
import { defineComponent, onMounted, onUnmounted, ref } from 'vue'
import Flickity from 'flickity'
export default defineComponent({
props: {
options: Object,
},
setup(props) {
let flickity: typeof Flickity | null = null
const root = ref<HTMLElement | null>(null)
onMounted(() => flickity = new Flickity(root.value as HTMLElement, props.options))
onUnmounted(() => flickity?.destroy())
return {
root,
append(element: HTMLElement) {
flickity?.append(element);
flickity?.select(-1)
}
}
},
})
</script>
<style scoped>
@import '~flickity/dist/flickity.css';
.flickity .carousel {
background: #EEE;
margin-bottom: 40px;
}
.flickity::v-deep .carousel-cell {
height: 200px;
width: 25%;
margin: 0 10px;
background: #6C6;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
}
.carousel-cell {
background-color: #248742;
width: 300px; /* full width */
height: 160px; /* height of carousel */
margin-right: 10px;
}
/* position dots in carousel */
.flickity-page-dots {
bottom: 0px;
}
/* white circles */
.flickity-page-dots .dot {
width: 12px;
height: 12px;
opacity: 1;
background: white;
border: 2px solid white;
}
/* fill-in selected dot */
.sr-flickity-page-dots .dot.is-selected {
background: white;
}
/* no circle */
.flickity-button {
background: transparent;
}
/* big previous & next buttons */
.flickity-prev-next-button {
width: 100px;
height: 100px;
}
/* icon color */
.flickity-button-icon {
fill: white;
}
/* hide disabled button */
.flickity-button:disabled {
display: none;
}
</style>
flickity.d.ts
interface IFlickity {
new (el: string | HTMLElement, options?: Record<string, unknown>): this;
append(element: HTMLElement);
destroy();
select(id: string | number);
}
declare module 'flickity' {
const Flickity: IFlickity;
export = Flickity;
}
this.$refs.flickity
is the Flickity.vue
component, and not the underlying flickity
instance. Your Flickity.vue
component has no on()
method, which causes the error you observed.
You could expose the flickity
instance's on()
method via a component method in Flickity.vue
:
// Flickity.vue
export default defineComponent({
setup() {
return {
on(eventName: string, listener: () => void) {
flickity?.on(eventName, listener)
}
}
}
})
And update the type declaration to add on()
:
// flickity.d.ts
interface IFlickity {
//...
on(eventName: string, listener: () => void)
}
declare module 'flickity' {
const Flickity: IFlickity;
export = Flickity;
}