Search code examples
alpine.jsdexie

How to plug existing Observables into Alpine.js (liveQuery from Dexie.js)


How are existing reactive observables connected to Alpine.js?

The Dexie.js website lists a few examples with React and Svelte but how would I use Dexie.js liveQuery with Alpine.js? Is it as simple as passing the variable to x-data?


Solution

  • You cannot pass directly a liveQuery object to an Alpine.js property because it will lose reactivity. We need to create a small wrapper that updates Alpine.js data when a liveQuery returns new data. Here I provide a small example that uses a products table, the Alpine.js component just lists the products and there's a small form that can add new products to the DB.

    Example database definition in db.js:

    import Dexie from 'dexie'
    
    export const db = new Dexie('myDatabase')
    db.version(1).stores({
      products: '++id, name, color',
    })  
    

    In main.js we make db and liveQuery global:

    import Alpine from 'alpinejs'
    
    import { liveQuery } from "dexie"
    window.liveQuery = liveQuery
    
    import { db } from './db'
    window.db = db
    
    window.Alpine = Alpine
    window.Alpine.start()
    

    The example Alpine.js component:

    <div x-data="productsComponent">
      <div>
        <input type="text" x-model="name" placeholder="Name" />
        <input type="text" x-model="color" placeholder="Color" />
        <button @click="add">Add product</button>
      </div>
    
      <div>
        <h2>Products</h2>
        <template x-for="p in products">
          <div x-text="`ID: ${p.id} Name: ${p.name} Color: ${p.color}`"></div>
        </template>
      </div>
    </div>
    
    <script>
    document.addEventListener('alpine:init', () => {
      Alpine.data('productsComponent', () => ({
        products: [],
        name: '',
        color: '',
    
        observe(dataName, observable) {
          const subscription = observable.subscribe({
            next: val => {this[dataName] = val}
          })
        },
    
        init() {
          this.observe('products', liveQuery(() => db.products.toArray()))
        },
    
        async add() {
          const id = await db.products.add({
            name: this.name,
            color: this.color,
          })
          this.name = ''
          this.color = ''
        }
      }))
    })
    </script>
    

    In the observe method we subscribe the specific liveQuery event and update the Alpine.js data when it changes.