Reputation: 51
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 ...
}
Upvotes: 4
Views: 3317
Reputation: 64
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
Upvotes: 3
Reputation: 5186
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
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.
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;
Upvotes: 6