Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stimulus js Cross Controller events context

I have two, independent, stimulus controllers. One that manages a table and one that is used to trigger a new row for the table.

I have a button on the page that calls the turbo-table-new-row#show function, which does dispatch the event and the turbo-table#show function is called, but I don't seem to have access to the turbo-table's 'this', so I'm unable to access the targets, values, etc...

If I move the button into the turbo-table's scope, I don't need the second controller, and everything works. However, from the UI perspective, this isn't workable.

How do I get access to the receiving controller's 'this' after receiving the event?

 <div data-controller="turbo-table-new-row turbo-table"
      data-action="turbo-table-new-row:show->turbo-table#display">

    <button data-action="click->turbo-table-new-row#show">
 </div> 
 // turbo-table-new-row-controller

  show(e) {
   this.dispatch("show", { detail: { url: e.params.url} })
  }
  // turbo-table-controller
show(e) {
        console.log("[turbo_table] - turbo-table-new-row->show event")
        console.log(e.detail.url)

  // I don't have access to the turbo-table-contoller 'this'

  this.hasPanelTarget ...

}
like image 458
studeba Avatar asked Oct 24 '25 16:10

studeba


2 Answers

It should be possible to dispatch an event from one controller and read it in another controller when not in the same DOM element.

When you dispatch an event from JavaScript, it will bubble up the DOM tree through to the window. You can listen to global events with the @window action descriptor to catch any events that have bubbled up outside of the controller's DOM tree.

See

  • https://stimulus.hotwired.dev/reference/actions#global-events
  • https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#parameters

You may need to be careful to check that it is the 'right' event you want, but as a basic set up you need to add the @window on your data action.

Example

Here is a working end to end example, not turbo-links, where the table controller is not a parent of your add row button (it is a sibling). Using the window event listener approach we can listen to events outside of the DOM tree.

Once the event is received in your tables controller, the class method should have access to that controller's this without issue.

If you want to access the original trigger button's target element you can do this via event.target.

<main>
  <table data-controller="table" data-action="table-action:add@window->table#show">
    <thead>
      <tr>
        <th data-table-target="status"></th>
      </tr>
    </thead>
    <tr data-table-target="row">
      <td>Item 1</td>
    </tr>
    <tr data-table-target="row">
      <td>Item 2</td>
    </tr>
    <tr data-table-target="row">
      <td>Item 3</td>
    </tr>
    <tr data-table-target="row">
      <td>Item 4</td>
    </tr>
  </table>
  <div>
    <button data-controller="table-action" data-action="table-action#add"
      data-table-action-url-param="https://path.to.tables/">
      Add row
    </button>
  </div>
</main>
import { Controller } from '@hotwired/stimulus';

class TableController extends Controller {
  static targets = ['row', 'status'];

  show({ detail: { url } = {}, target }) {
    console.log('event.target - the button that triggered the click', event.target);
    if (url) {
      const rowCount = this.rowTargets.length;
      this.statusTarget.innerText = `Request from: ${url}, there are ${rowCount} rows.`;
    }
  }
}

export default TableController;
import { Controller } from '@hotwired/stimulus';

class TableActionController extends Controller {
  add({ params }) {
    this.dispatch('add', {
      detail: { ...params },
      bubbles: true,
      cancelable: false,
    });
  }
}

export default TableActionController;
like image 113
LB Ben Johnston Avatar answered Oct 26 '25 05:10

LB Ben Johnston


From v3.2.0 you can use Outlets API for cross-controller communication and coordination as an alternative to dispatching custom events on controller elements.

See

  • https://stimulus.hotwired.dev/reference/outlets
like image 25
achmiral Avatar answered Oct 26 '25 06:10

achmiral