Single Responsibility Principle
by Julian Rubisch
Especially when applying Stimulus to your application for the first time, it is tempting to write your controllers in a page controller style, resulting in a disjointed accumulation of unrelated functionality. Resist that temptation - try to write reusable controllers.
Below is a shortened juxtaposition of what that could look like.
Bad
<!– page.html –>
<div data-controller=“page”>
<form action=“/” data-page-target=“form”></form>
<div data-page-target=“modal” class=“modal”></div>
</div>
<!-- page.html --> <div data-controller="page"> <form action="/" data-page-target="form"></form> <div data-page-target="modal" class="modal"></div> </div>
// page_controller.js
import { Controller } from “@hotwired/stimulus”;
export default class extends Controller {
static targets = [“modal”, “form”];
openModal() {
this.modalTarget.classList.add(“open”);
}
submitForm() {
this.formTarget.submit();
}
}
// page_controller.js import { Controller } from "@hotwired/stimulus"; export default class extends Controller { static targets = ["modal", "form"]; openModal() { this.modalTarget.classList.add("open"); } submitForm() { this.formTarget.submit(); } }
Good
<!– page.html –>
<div>
<form action=“/” data-controller=“form”></form>
<div data-controller=“modal” class=“modal”></div>
</div>
<!-- page.html --> <div> <form action="/" data-controller="form"></form> <div data-controller="modal" class="modal"></div> </div>
// modal_controller.js
import { Controller } from “@hotwired/stimulus”;
export default class extends Controller {
open() {
this.element.classList.add(“open”);
}
}
// modal_controller.js import { Controller } from "@hotwired/stimulus"; export default class extends Controller { open() { this.element.classList.add("open"); } }
// form_controller.js
import { Controller } from “@hotwired/stimulus”;
export default class extends Controller {
submit() {
this.element.submit();
}
}
// form_controller.js import { Controller } from "@hotwired/stimulus"; export default class extends Controller { submit() { this.element.submit(); } }
Rationale
Books have been written about this very single topic, but let it be said that classes/modules that serve a single responsibility are
- easy to reuse - in our example above the
page_controller
can only be used on this very page (or one with the same structure), whereasmodal_controller
andform_controller
could be used on any modal or form element, and - easy to change - because every responsibility has a single point of realization, changes are cheap: instead of having to implement a new functionality in several places, there’s only one spot where you have to install the new behavior.
Reference
- Practical Object Oriented Design by Sandi Metz
- Wikipedia