Stimulus to grab elements from DOM


#1

I have elements in my view that are not part of the data-controller or target but are needed in my stimulus controller.

For example, when a field on a form changes, I want the Stimuls controller to be triggered and that controller gathers the values from other fields in the form and sends them off to Rails controllers via AJAX. In the example below, when the second collection select field changes I want to send it’s value and the rep value off to a Rails controller to calculate a budget.

      <%= f.collection_select :rep_id, @active_reps, :lid, :full_name,
          {:include_blank => false},
          {
            :data => {
              target: "rep-quarter.rep"
            }
          } %>
      <%= f.collection_select :financial_quarter_id, @financial_quarters, :lid, :name,
          {:include_blank => false},
          {
            :data => {
              controller: "rep-quarter",
              target: "rep-quarter.financial-quarter"
              action: "change->rep-quarter#calculate",
            }
          } %>
        <%= f.text_field :budget, hide_label: true,
            data: { target: "rep-quarter.output" } %>

Currently I get those other form values off the DOM by

document.getElementById(“rep”).value

I have tried doing this using this.targets.find("rep").value but this returns an error because I guess this is referring too the controller target where the action was triggered from (in this case ‘quarter’)

So should I be getting these other element values using document or does Stimulus have a way to get other elements off the DOM that aren’t part of the data-controller?

Sorry if my question doesn’t make sense. I’m still beginner level when it comes to DOM and Javascript.


#2

This is a good question and I wanna know a good answer for this. I’m new to this too, maybe almost everyone here is, just trying to work with this.

What I think is you can only access values inside that controller, you can pass values from the controller with the example from docs <div data-controller="slideshow" data-slideshow-index="1"> in https://stimulusjs.org/handbook/managing-state

But I think using the document to get a value it’s no big deal in this case (correct me if I’m wrong) because here if you have the same controller more than one place you go to grab the same value from the document.
Now if you have different values or the controller are in different pages, you probably want to pass it trough with the example of data-slideshow-index.


#3

Using document is fine, you can also use jQuery if you so desire. AFAIK there is no such functionality in stimulus, which is fine, it’s not what it’s meant for.


#4

I believe the Stimulus philosophy is to treat this.element as its DOM scope, so targets cannot be auto-loaded from outside that scope. You have a few options:

  • Refactor your controller so that it’s declared with data-controller on the outermost part of the DOM to which the controller needs automatic targets access.
  • Do exactly what you’ve described, which is to manually parse DOM elements outside of the controller’s DOM scope using jQuery, raw JavaScript, or anything else.

Both solutions are valid depending on your situation. For instance, if a little widget deeply nested in your page also needs access to a global message notification area in a distant DOM node, then manually parsing may make more sense; you probably don’t want to have to declare your controller on the body element or the outermost wrapper.

But if you’re talking about a form, and you’re currently declaring your controller on an element inside that form, why not rewrite the controller so its element is the form itself?