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.

toggle.element2 remains visible when toggle.element1 is toggled to hidden, is there a way to make it behave like a nested menu?

You could nest the two elements in a common parent (DOM) element, and add the controller to that parent. Then the target element would reach up to the common parent and perform the change. Is that what you’re thinking of here?

Walter

I managed to get what I want to work, but I’m not sure if it is “legal” or if there are any implications.
What I want is that if I expand menu, then language, and if I were to click outside menu, everything should collapse. It does, but I don’t know why exactly.

<div id="menu" data-controller="toggle">
    <nav data-toggle-target="toggleable">
    <a>Orders</a>
    <div data-controller="toggle">
        <button>language</button>
        <div data-toggle-target="toggleable">
            ...
        </div>
    </div>
    </nav>
</div>