omarluq/stimulus-store

Evolving stimulus-store

Closed this issue · 1 comments

Add an api to bind a store value as data attribute to an html element (within the scope of a controller element)
e.g.

  import { Controller } from '@hotwired/stimulus';
  import { useStore } from 'stimulus-store';
  import { counterStore } from './stores/counter';

  export default class extends Controller {
    static stores = [counterStore]

    connect() {
      useStore(this);
    }
  }
<div controller='my-controller'>
  <div my-controller-counterStore-value=<%= @likes.count %>></div>
</div>
  • changes to the store value attribute on the bond element will automatically qualify as a setStoreValue
  • if a new element with store attribute enters the dom it will automatically qualify as a setStoreValue

e.g implementation

export function useStore(controller: StoreController) {
  const stores: Store[] | undefined = controller.constructor?.stores;
  // Keep track of MutationObservers to cleanup later
  const mutationObservers: MutationObserver[] = [];

  stores?.forEach((store) => {
      const controllerName: string = controller.identifier.replace(/_/g, '-');
      const storeName: string = camelize(store.name.toString().slice(7, -1), false);
      const attributeToWatch: string = `data-${controllerName}-${storeName}-value`;
      
      // Setup a MutationObserver for each store-related attribute
      const observer = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
          if (mutation.type === "attributes" && mutation.attributeName === attributeToWatch) {
            // Retrieve the new value from the mutated attribute
            const newValue: StoreValue = mutation.target.getAttribute(attributeToWatch)
            // Update the store with the new value
            store.set(newValue);
          }
        });
      });
  
      // Observe changes in the controller's element and its children
      observer.observe(controller.element, {
        attributes: true,
        subtree: true,
        attributeFilter: [attributeToWatch],
      });
      
      mutationObservers.push(observer);
  }
  
  const originalDisconnect = controller.disconnect.bind(controller);
  controller.disconnect = () => {
    // Disconnect all mutation observers
    mutationObservers.forEach((observer) => observer.disconnect());
    // Call the original disconnect method
    originalDisconnect();
  };
}

👋 Thanks for raising an issue.

We will review it and get back to you as soon as possible! 🚀