Using Flickity API in Vue 3 with Typescript Integration

I have encountered an issue with implementing Flickity in my Vue 3 application. Everything works perfectly fine when using a static HTML carousel with fixed cells. However, I am facing difficulties when attempting to dynamically add cells during runtime using the Flickity API.

While following the append() example, I encounter the following error:

flickity.js?1385:72 Bad element for Flickity: .carousel

This error appears in the inspector during runtime. I have tried various solutions from Stack Overflow and GitHub, but none of them seem to be working successfully. It seems like there are TypeScript-related issues with the Flickity library. I have also installed @types/flickity.

What steps should I take to resolve the issue with my append logic as shown below?

<template>
        <div class="row">
          <div class="col d-block m-auto payment-option">
            <flickity ref="carousel" :options="flickityOptions">
            </flickity>
          </div>
      </div>
</template>

<script lang="ts">
import {defineComponent} from "vue";
//import Flickity from 'vue-flickity/src/flickity.vue';
import Flickity from 'flickity'
export default defineComponent({
  name: "PageName",
  components: {
    Flickity
  },
  data() {
    return {
      flickityOptions: {
        initialIndex: 3,
        prevNextButtons: false,
        pageDots: true,
        wrapAround: true
      }
    };
  },
  methods: {
    createBankAccountCarousel(flkty: Flickity) {
      flkty.append(this.makeFlickityCell())
    },
    makeFlickityCell() {
      const cell = document.createElement('div');
      cell.className = 'carousel-cell'
      cell.textContent = "Hi"
      return cell
    }
  },
  mounted() {
    let flkty = new Flickity(this.$refs.carousel)
    this.createBankAccountCarousel(flkty)
  }
});
</script>

Answer №1

It seems like you were attempting to use vue-flickity with Vue 3, but that component was designed for Vue 2.

You have the option to create your own Flickity component in Vue 3:

  1. Create a file named Flickity.vue with a template and script that includes a template ref on the root element, and a slot to receive .carousel-cell elements:

    <template>
      <!-- 👇 template ref -->
      <div ref="root" class="flickity">
        <slot />
      </div>
    </template>
    
    <script lang="ts">
    import { defineComponent, ref } from 'vue'
    
    export default defineComponent({
      setup() {
        const root = ref<HTMLElement | null>(null) // reference to template ref named "root"
        return {
          root,
        }
      }
    })
    </script>
    
  2. Add an options prop that will be passed to the Flickity constructor later:

    <script lang="ts">
    import { defineComponent } from 'vue'
    
    export default defineComponent({
      props: {
        options: Object,
      }
    })
    </script>
    
  3. Incorporate the mounted hook in the component to initialize the Flickity instance using the "root" template ref and the provided options prop; and in unmounted, make sure to destroy the Flickity instance:

    <script lang="ts">
    import { defineComponent, onMounted, onUnmounted } from 'vue'
    import Flickity from 'flickity'
    
    export default defineComponent({
      setup(props) {
        let flickity: typeof Flickity | null = null
        onMounted(() => flickity = new Flickity(root.value as HTMLElement, props.options))
        onUnmounted(() => flickity?.destroy())
      }
    })
    </script>
    
  4. Introduce a method called "append" to enable appending new elements to the carousel:

    <script lang="ts">
    import { defineComponent } from 'vue'
    import Flickity from 'flickity'
    
    export default defineComponent({
      setup() {
        let flickity: typeof Flickity | null = null
        return {
          append(element: HTMLElement) {
            flickity?.append(element)
            flickity?.select(-1)
          }
        }
      }
    })
    </script>
    
  5. Create a file named src/flickity.d.ts containing specific type declarations for better indexing (if you are using VS Code, consider restarting the IDE for proper recognition):

    declare module 'flickity' {
      const Flickity: {
        new (el: string | HTMLElement, options?: Record<string, unknown>): this
        append(element: HTMLElement)
        destroy()
        select(id: string | number)
      }
      export = Flickity
    }
    
  6. Include the following <style> block to incorporate default styles from flickity and style the .carousel-cell elements received in the slot:

    <style scoped>
    @import '~flickity/dist/flickity.css';
    
    .flickity .carousel {
      background: #EEE;
      margin-bottom: 40px;
    }
    /* use ::v-deep to access slot elements */
    .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;
    }
    </style>
    

Example Usage:

<template>
  <div class="app">
    <flickity ref="flickity" :options="flickityOptions">
      <div class="carousel-cell">1</div>
      <div class="carousel-cell">2</div>
      <div class="carousel-cell">3</div>
    </flickity>
    <div class="actions">
      <button @click="addElement">Append element</button>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import Flickity from './components/Flickity.vue'

export default defineComponent({
  name: 'App',
  components: {
    Flickity
  },
  data() {
    return {
      flickityOptions: {
        pageDots: true,
        wrapAround: true,
      }
    }
  },
  methods: {
    addElement() {
      // 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 = 'Hi'
      return cell
    }
  }
})
</script>

<style scoped>
.app {
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  height: 50vh;
}
</style>

demo

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Modifying CSS styles in JavaScript based on the user's browser restrictions

My CSS style looks like this: button.gradient { background: -moz-linear-gradient(top, #00ff00 0%, #009900 50%, #00dd00); background: -webkit-gradient(linear, left top, left bottom, from(#00ff00), color-stop(0.50, #009900), to(#00dd00) ...

Unable to stop submission as a result of ajax verification

As someone new to java and jquery, I am facing a challenge with the code below: $(document).ready(function() { //Prevent SUBMIT if Session setting = On (Ajax) $('#formID').submit(function(e) { var prevent = false; $.ajax({ type: ...

Display components created for children above the components created for parents

I am looking to render a component with a different route without it covering the entire page. For example, let's say I click on a question from a list on Stack Overflow, and then I want an animated modal to appear from right to left without changing ...

Blend the power of Dynamic classes with data binders in Vue.js

Recently, I've been working on a v-for loop in HTML that looks like this: <ul v-for="(item, index) in openweathermap.list"> <li>{{item.dt_txt}}</li> <li>{{item.weather[0].description}}</li> <li>{{item.w ...

Error: The specified JSON path for Ajax request could not be

Although my expertise lies in C++, I must confess that my knowledge about web development is quite limited. Therefore, please bear in mind that my question requires a simple answer. Recently, I stumbled upon a fascinating C++ library for creating a web se ...

IIS Alert: Missing Images, CSS, and Scripts!

When I tried to publish my website using IIS, I encountered the error message Cannot read configuration file due to insufficient permissions. After attempting to add permissions for IIS_USRS and realizing that this user does not exist on my computer runnin ...

Move and place multimedia items using IE's drag and drop feature

Is there a way to enable the drag and drop feature across different browsers or windows using HTML5's native DnD API? I have noticed that when I set the data type to 'text' in Internet Explorer, it functions properly. However, if I attempt t ...

Concealing two div elements based on string content

I am working on a unique Wordpress blog post layout that showcases personnel profile cards, displaying 12 individuals at a time. Depending on whether or not a person has a Twitter handle entered in the backend, certain elements should either be shown or hi ...

Angular - the ngFor directive causing function to be executed repeatedly

I need help with a template: <mat-card *ngFor="let cargo of cargos" class="cont-mat"> ... <mat-checkbox *ngFor="let truck of (retrievingTrucksByUserIdAndRules(cargo.id) | async)" formControlName="truckId" ...

The initial values of Typescript class members are merged directly into the class constructor

This issue has been well documented (check out Initializing variables inline during declaration vs in the constructor in Angular with TS on SO for reference), but it can lead to challenging memory problems. Take a look at the example below class Bar { ...

The authentication method "discord" is not recognized

Currently, I am working on implementing discord authentication using passport. Originally, everything was functioning correctly, but now it seems to have encountered an issue which I cannot identify. auth.js const express = require('express') co ...

Good day extract a collection of articles

I am trying to parse out the date and full URL from articles. const cheerio = require('cheerio'); const request = require('request'); const resolveRelative = require('resolve-relative-url'); request('https://www.m ...

A guide to setting defaultValue dynamically in React Hook Form

Presently, I am facing an issue with editing the Product page. The props values are fetched through an API and sent from the parent component. However, I am struggling to set this value to my Datepicker input. This is because the defaultValue function from ...

TinyMCE file multimedia upload feature allows users to easily add audio, video

I am looking to enhance the functionality of my TinyMCE Editor by enabling file uploads for audio/video and images. Although image uploading is functioning properly, I am encountering issues with other types of files. Despite setting up pickers throughout, ...

Displaying variables in JavaScript HTML

<script type ="text/javascript"> var current = 0; </script> <h3 style={{marginTop: '10', textAlign: 'center'}}><b>Current Status: <script type="text/javascript">document.write(cur ...

How can I use an input array to filter data in mongodb?

When receiving input from the front-end, it looks like this: { "options":[ { "optionId":"5ebbe0f56b197f36fc472168" }, { "optionId":"5ebbe1aa6b197f36fc47216e" } ] } The goal is to filter the data in a way that ...

Is there a way to automatically close one menu when another is opened by clicking?

When all other search results have failed to solve my issue, I resort to posting my own question. My problem involves opening hidden submenus in a website's main menu. However, when I open multiple submenus, they just stack above each other. Ideally, ...

Unexpected state being returned by Vuex

I am encountering an issue with a pop-up modal that is not behaving as expected. The condition for the pop-up to appear is if the user has no transactions, which is determined by checking the length of the depositHistory array. If the length is greater tha ...

Interactive image sliders in a Netflix-inspired style with live previews on hover, fully compatible with Bootstrap framework

I'm looking for suggestions on Twitter Bootstrap compatible jquery plugins that can create a Netflix-style continuously scrolling image carousel with mouse-over functionality. I've tried the carousel included in the Bootstrap JS library, but it r ...

Guide on how to fetch Laravel's CSRF token with a separate Vue frontend

Is it possible to transfer the Laravel csrf token to Vue when the backend and frontend are located in separate directories and subdomains? I am working on an application that requires a distinct separation between the backend and frontend for organizationa ...