Why `this.fooTarget` instead of `this.targets.foo` (or `this.targets.get('foo')`)?

I’m learning stimulus and one detail that jumped to my mind was the differences between the API for handling targets (i.e. this.nameTarget) and the API for “state data” (this.data.get("index")).

It would seem to me that using a namespace for targets (this.targets.index or this.targets.get('index')) could have several advantages over the current implementation:

  • this.targets would allow lazy loading: targets would start “empty” and fetch each target when it is first used instead on fetching them all from the DOM on connection (as I am assuming is done now).
  • The previous point would allow removing the static targets variable from the controllers.
  • There would be a place to add common functions for targets. Iterate over all targets, for example.
  • The API would look more cohesive and, in my opinion, easier to learn.

I think I might be missing something crucial.

Thanks for releasing this library to the world!

Target properties are actually just shortcuts to an underlying this.targets API (that still exists, but is undocumented). Here’s the change that introduced them in v1.0: Linked Target Properties by javan · Pull Request #61 · hotwired/stimulus · GitHub.

Prior to 1.0, we would often create ad-hoc properties like these in our controllers:

export default class extends Controller {
  connect() {
    console.log(this.nameTarget)
  }

  get nameTarget() {
    return this.targets.find("name")
  }
}

As time went on, a mixed bag of naming conventions appeared in our application:

  get nameTarget() {
    return this.targets.find("name")
  }

  // Or…

  get nameElement() {
    return this.targets.find("name")
  }

  // Or…

  get nameInput() {
    return this.targets.find("name")
  }

So, we decided to formalize that pattern and enforce a consistent naming convention.

It might look that way, but they’re actually computed properties that query the DOM on-demand.

Great questions!

1 Like

Thanks a lot for the very detailed answer! Yes, I see how providing some default structure for shortcuts might be a good idea.

Why is the static targets property needed, then? Why can’t this.targets.find("foo") directly query the DOM for [data-target="my-controller.foo"] the first time it is called?

The names in that array are used to create target properties.

That’s exactly what happens under the hood. No DOM queries are performed until you access a target property. Accessing this.fooTarget calls this.targets.find("foo") and returns the result every time.

1 Like

Understood. Thanks for taking the time to explain.