Hotwire Discussion

Nesting the same controller within itself

Hi, I have an issue where I want to be able to add the same functionality to a HTML element, and another HTML element which is nested within it. Essentially it would be:

<div data-controller="toggle">
  <div data-target="toggle.element1">
    Some Content

    <div data-controller="toggle">
     <div data-target="toggle.element1">
       Some Nested Content
      </div>
    </div>
  
    <div data-target="toggle.element2">
        Some Nested Content
    </div>
    </div>
  </div>

  <div data-target="toggle.element2">
      Some Content
  </div>
</div>

But the problem is that the targets could now be referring to two different instantiations of the same controller. I don’t know how to solve this without going against the Stimulus idea of having behaviour-based controllers, and just giving them each their own controller.

Don’t use target for this scenario, use an element selector instead. I’ve seen many think with Stimulus that you should “only” use stimulus target as a selector. That is not the case. The hey.com team uses javascript methods other than target to find and element all throughout their controllers when target does not make sense. As with all things in turbo and stimulus, start with the simple versions and progress to more complex if the simple versions do not work.

Something like the following (untested).

<script type="text/javascript" charset="utf-8">
  class HiddenController extends StimulusController {
    static values = {target: String}

    hide(event) {
      event.preventDefault();
      this.toggleHidden(true)
    }

    show(event) {
      event.preventDefault();
      this.toggleHidden(false)
    }

    toggle(event) {
      event.preventDefault();
      this.toggleHidden()
    }

    toggleHidden(force){
      const targets = document.querySelectorAll(this.targetValue);
      targets. forEach((target) => target.toggleAttribute('hidden', force))
    }
  }
</script>

<a data-controller="hidden" data-hidden-target-value="#toggle-my-id" data-action="hidden#toggle">Toggle 1</a>
<div>
  Some Content
  <div id="toggle-my-id" hidden>
    Some Nested Content
    <a data-controller="hidden" data-hidden-target-value=".toggle-my-class" data-action="hidden#toggle">Toggle 2</a>
  </div>

  <div class="toggle-my-class" hidden>
    Some Nested Content
  </div>
</div>

<div class="toggle-my-class" hidden>
  Some Content
</div>
1 Like

Great solution, thank you.