Configurable Controllers

by Julian Rubisch

Bad

<!-- index.html -->
<a href="#" data-controller="toggle" data-action="click->toggle#toggle">
  Toggle
</a>

// toggle_controller.js
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  toggle(e) {
    e.preventDefault();
    this.element.classList.toggle("active");
  }
}

Good

<!-- index.html -->
<a href="#" data-controller="toggle" data-action="click->toggle#toggle" data-toggle-active-class="active">
  Toggle
</a>

// toggle_controller.js
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static classes = ["active"];

  toggle(e) {
    e.preventDefault();
    this.element.classList.toggle(this.activeClass);
  }
}

Rationale

Late binding of dependencies (in the simplest case above, the value of a CSS class. Other examples may include the ID of a DOM element or a CSS selector) ensures that your controller is re-usable across multiple use cases.

Simply moving that value out of the controller into a dataset property will make your controllers more flexible, and you’ll have to write less specialized JavaScript in favor of more adaptable controllers that allow dependencies to be injected and thus obey the Single Reponsibility Principle.