Obromios
Obromios

Reputation: 16373

How to pass a data value to a stimulus controller upon a load event

I have a Stimulus controller

# app/javascript/controllers/clubs_controller.js
// handles micropost javascripts
import { Controller } from "stimulus"
import Rails from '@rails/ujs';
export default class extends Controller {

  connect() {
    }

    wait_for_clubs(event) {
      event.preventDefault()
      console.log("in wait_for_clubs", event.target)
      const id = event.target.dataset.value
      console.log("id", id)
      }
    }
}

and html code

<div data-controller="clubs">
  <span data-action="load@window->clubs#wait_for_clubs" data-value='<%= @user.id %>'>Waiting for Clubs to be prepared</span>
</div>

with the idea that when the page is loaded, the controller has the id of the user. When I reload the page, I see the following error in the console

TypeError: Cannot read property 'value' of undefined

which is triggered by the var id = event.target.dataset.value; statement.

However if I change the event from load@window to click, then when I click the span, the code works and retrieves the id. How do I fix this?

Upvotes: 17

Views: 31988

Answers (2)

Dennis Hackethal
Dennis Hackethal

Reputation: 14275

Things have changed a bit since the currently accepted answer was submitted.

The docs now advise against reading data attributes directly (this.data.get("myValue")):

This might get the job done, but it’s clunky, requires us to make a decision about what to name the attribute, and doesn’t help us if we want to access the index again or increment it and persist the result in the DOM.

Instead, declare the values on the controller itself. Doing so has the added benefit of coercing the attribute to the specified type:

<div data-controller="slideshow" data-slideshow-index-value="1">
export default class extends Controller {
  static values = { index: Number }

  initialize() {
    console.log(this.indexValue)
    console.log(typeof this.indexValue)
  }

  // …
}

Upvotes: 4

Ray
Ray

Reputation: 431

If you are trying to pass data to a Stimulus controller that exists on the DOM when it loads, the Stimulus docs recommend using the built-in data API.

In your case, that would mean adding a data-clubs-my-value attribute to the DOM element where you assign the data-controller attribute, like so:

<div data-controller="clubs"
     data-clubs-my-value="<%= @user.id %>">
</div>

and then you can get this value from your Stimulus controller by calling this.data.get("myValue")

As to why data-action didn't work...

The Stimulus docs do allow for Global Events so this event is being triggered. But when this happens, the triggering event.target is window, not your <span>, so when you call event.target.dataset, it is looking for the dataset of the window, which does not have your value.

If you want an action in your Stimulus controller to fire upon page load, the best way to handle this is generally by using the connect() method.

Upvotes: 32

Related Questions