Outlets

by Julian Rubisch

Inter-process communication has been a source of many band-aids in the past. Luckily with outlets we now have an official way to invoke logic across controller boundaries.

Good

<body
    data-controller="job-dashboard"
    data-job-dashboard-job-outlet=".job"
  >
  <button data-action="job-dashboard#refresh"></button>
  <ul>
    <li data-controller="job" class="job"></li>
  </ul>
</body>

// job_dashboard_controller.js
import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
  static outlets = ['job'];

  refresh() {
    this.jobOutlets.forEach((outlet) => {
      outlet.update({...})
    });
  }
}

// job_controller.js
import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
  update(data) {
    // ...
  }
}

Rationale

You have split your controllers according to the Single Responsibility Principe, and now want to send messages from one to another. This can occur, for example, in data-rich complex UIs like dashboards, data tables, media elements etc.

Contraindications

Tracking outlets via their selectors in the HTML can be tedious. The markup can become bloated and confusing. So I’d advise to use it sparingly, and look into custom events as an alternative.