Accessing data on targets

I often find myself in situations where I need to access data on targets in a conventionalized way. How do you folks typically handle this?

Controllers can leverage the Data API to manage state and handle data, but targets don’t have the same opportunity.

It would be neat to have data attributes bound to targets using an extended version of the data map format, like data-[controller]-[target]-[key], which would make these targets

<div data-controller="todo">
  <input type="checkbox" data-target="todo.item" data-todo-item-id="1">
  <input type="checkbox" data-target="todo.item" data-todo-item-id="2">
  <input type="checkbox" data-target="todo.item" data-todo-item-id="3">
</div>

accessible with som sort of (made up and slightly awkward) API

// todo_controller.js
someAction(event) {
  this.data.getDataMapForElementAndTargetName(this.itemTarget, 'item').get('id')
  // or
  this.data.getDataMapForElementAndTargetName(event.currentTarget, 'item').get('id')
}

Since DOM elements can be more than one kind of target it would required to pass the target name along with the element to get the correct data map.

However, since this doesn’t exist, I’m currently torn between the following workarounds:

Controllerizing the targets
In some scenarios it would be possible to extract the behavior to a child controller which could leverage the formalized Data API:

<div data-controller="todo">
  <input type="checkbox" data-controller="todo-item" data-target="todo.item" data-todo-item-id="1">
</div>
// todo_item_controller.js
someAction() {
  this.data.get('id')
}

Pros: Uses the existing Data API
Cons: Can only be used when behavior is extractable and not tied to a parent controller

Using the dataset property
There’s nothing preventing us from sticking to a naming convention and accessing the data values natively in a controller action:

<div data-controller="todo">
  <input type="checkbox" data-target="todo.item" data-todo-item-id="1">
</div>
// todo_controller.js
someAction() {
  this.itemTarget.dataset.todoItemId
}

Pros: No additional work needs to be done
Cons: Lacks the enforced naming convention

I’d love to hear your suggestions and thoughts on this!

1 Like

With the current api the easiest is to use the target or currentTarget that is passed through the event
https://stimulusjs.org/reference/actions#event-objects

<div data-controller="todo">
  <input type="checkbox" data-action
="todo#someAction" data-target="todo.item" data-id="1">
</div>
// todo_controller.js
someAction(e) {
  const id = e.target.dataset.Id
  console.log(id) // 1
}
2 Likes

Thanks, @adrienpoly! I often go down that road too, but I kinda miss some sort of naming convention on the target data attributes… it’s not obvious that data-id is related to the todo.item target.

For anyone trying this out.

e.target.dataset.Id

won’t work. It has to be camelcase starting with downcase:

e.target.dataset.id

since then there is a built in solution now with the action param API