how to change a alpinejs data value with stimulus controller

67 views Asked by At

I am trying to update an alpine x-data value with my stimulus controller

The final goal being to trigger the openDetail on a leaflet marker

My controller

import {Controller} from '@hotwired/stimulus'
import L from 'leaflet'
import Alpine from 'alpinejs'

/**
 *
 */
export default class extends Controller {

    connect() {
        Alpine.data('isOpen', false)
        window.Alpine = Alpine
        Alpine.start()
        this.alpine = Alpine


    openDetail() {
        
        this.alpine.data('isOpen', true))

    }
}

My template

 <div x-data="{ isOpen: false}" data-controller="alpine">
  <button data-action="click->alpine#toggle" @click="isOpen = true">
    Toggle
  </button>
  <p x-show="isOpen" x-cloak>Content to toggle</p>
</div>

Thanks

1

There are 1 answers

0
LB Ben Johnston On

Unless you are required to use Stimulus, I would recommend you only use one library (Stimulus OR Alpine).

If your main project uses Alpine you could leverage Alpine data or bind to attach custom behaviour to any element.

See https://alpinejs.dev/globals/alpine-data or https://alpinejs.dev/globals/alpine-bind

<div x-data="{ isOpen: false}">
  <button type="button" @click="toggle">Toggle</button>
  <p x-show="isOpen" x-cloak>
    Content to toggle
  </p>
  <div x-ref="leaflet"></div>
</div>
import L from 'leaflet';
import Alpine from 'alpinejs';

document.addEventListener('alpine:init', () => {
    Alpine.data('dropdown', () => ({
        isOpen: false,
        leafletLoaded: false,
        toggle() {
            this.isOpen = ! this.isOpen
            this.$refs.leaflet.innerHTML = 'Loading...';
            this.loadLeaflet();
        },
        loadLeaflet() {
            if (this.leafletLoaded) return;
            this.leafletLoaded = true;
            // do something with 
            const map = L.map(this.$refs.leaflet)//... etc
        }
    }));
});

This lets us use Alpine without needing to code all the behaviour in the DOM, instead we can 'pull up' to a JS file that will manage the content and usage of the third party library.

Alternatively, you do the entire feature with Stimulus only.

<div data-controller="leaflet">
  <button type="button" data-action="leaflet#toggle">Toggle</button>
  <p hidden data-leaflet-target="content">
    Content to toggle
  </p>
</div>
import { Controller } from '@hotwired/stimulus';

import L from 'leaflet';

export default class extends Controller {
  static targets = ['content'];

  connect() {
    this.leaflet = null;
  }

  toggle() {
    if (this.contentTarget.getAttribute('hidden')) {
      this.contentTarget.removeAttribute('hidden');
    } else {
      this.contentTarget.setAttribute('hidden', '');
    }

    if (this.leaflet) return;

    this.leaflet = this.contentTarget.innerHTML = L.map('...');
  }
}

A similar approach to the Alpine, at this point they are actually almost the same code but just different DOM attributes.

Do remember to include type="button" when using a button that does not submit a form, it will avoid having to preventDefault on the click and also is more accessible.

Finally, if you are doing something that toggles to open, it's best to ensure you use the semantic element that does this already. details has wide browser support and will avoid you needing to build out the JS code to make something toggle open & closed. https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details