Integrate stimulus with websockets


#1

Hey I was wondering what was the best way to integrate stimulus with websockets
What I am currently doing is that I have a websocket with some javascript updating the controller html attribute and I have the stimulus controller refreshing with a timer.

Here are my questions.

  • I see there are onlick, onsubmit handlers, are there other handlers ? (can you add some ? typically when my websocket receives new data I would like to trigger an event to refresh the element)

  • Is there a way to re-initialize the element ? or maybe deconnect it and reconnect it. Just thinking out loud here.

(detail, I’m using phoenix with elixir, but I think this is applicable for any other backend framework that supports websockets)


#2

Ok here is what I ended up doing in case it can help anybody

  • my websocket code is now firing an “update” event on update
  • The controller is listening to that update event and re-rendering the UI accordingly.

Here is what the code look like

channel.on('update', ({lastSyncedAt}) => {
    // this update is used on several elements with various controllers, so I need to get the controller name 
    // to update the proper attribute
    const constrollerName = elementToUpdate.getAttribute('data-controller');
    elementToUpdate.setAttribute(`data-${constrollerName}-last-synced-at`, lastSyncedAt);
   // I fire an "update" event on the element
    const updateEvent = new Event('update');
    elementToUpdate.dispatchEvent(updateEvent);
})
<div
          class="media-body trading_account_last_synced_at"
          data-controller="main-sidebar-trading-account"
          data-url-hash="<%= url_hash %>"
          <-- Here is where I am hooked up to the update event -->
          data-action="update->main-sidebar-trading-account#show"
          <-- this is the element I update with my websocket -->
          data-main-sidebar-trading-account-last-synced-at="<%= last_synced_at %>"
        >
          <span class="media-heading text-semibold"><%= name %></span>
          <span class="text-muted">
            <i
              class="icon-checkmark3 text-size-small text-success"
              style="margin-top: -1px; font-size: 13px;"
              data-target="main-sidebar-trading-account.checkMark"
            >
            </i>
              <%= account_number %>
          </span>
        </div>

hopefully the code is clear enough, let me know if anything doesn’t make sense


#3

digging into javascript api, I have made a little update to the code.
It turns out that by using CustomEvent, your events can have value. So with the websocket I just fire a custom event and let the controller take care of the update.
Here is how the code looks now.

channel.on('update', ({lastSyncedAt}) => {
    const updateEvent = new CustomEvent('update', {detail: lastSyncedAt});
    elementToUpdate.dispatchEvent(updateEvent);
  });

and the js controller is now just

set lastSyncedAt(value) {
    return this.data.set('last-synced-at', value);
  }

update({detail}) {
    this.lastSyncedAt = detail;
    this.show();
  }

and I just changed the event handler on the html so now

data-action="update->main-sidebar-trading-account#update"

the weird variable naming detail, comes from the CustomEvent api, you can only pass an object with a detail attribute.
Hope this helps someone