Hotwire Discussion

Stream can update only a single target id...so what about repeated elements?

Let’s say User count shows up 2-3 times on a page. A Stream can only update one of them since ids have to be unique per page. We’d have to contrive a different ID for each example, and return a stream template for each…even though it’s all the same element showing the same data.

Seems very not DRY. Or is there another way to do it? Seems like there should be an alternative to using element id. For example, typically with jQuery you would do something like class="js-hook" and can be used and targeted as often as desired.

2 Likes

This is a use case I’m running into all the time too! The same holds for updating a broadcast_to value from the model to multiple parts of the page.

1 Like

Hi,

You could have a hidden tag with a stimulus controller attach to it and responsible 1) of dispatching some event to all counters tag or 2) querySelectorAll counter tag and updating their value.

In both solution, you need to replace the hidden tag, using a turbo_stream, which update all counters on the page.

<turbo-stream action="replace" target="update_counters">
  <template>
    <div id="update_counters" data-controller="update-counters" data-update-counters-initial-count-value="13" hidden></div>
  </template>
</turbo-stream>

1 )

// update_counters_controller.js

import { Controller } from "stimulus";

export default class extends Controller {
  static values = {
    initialCount: Number
  }

  initialCountValueChanged() {
    const event = new CustomEvent("updateCounter", {
      detail: this.initialCountValue
    })

    window.dispatchEvent(event)
  }
}

and

// counter_controller.js

import { Controller } from "stimulus";

export default class extends Controller {
  update(e) {
    this.element.innerHTML = e.detail
  }
}

and

<div id="update_counters" data-controller="update-counters" data-update-counters-initial-count-value="2" hidden></div>

<div data-controller="counter" data-action="updateCounter@window->counter#update">0</div>
<div data-controller="counter" data-action="updateCounter@window->counter#update">0</div>

2 )

// update_counters_controller.js

import { Controller } from "stimulus";

export default class extends Controller {
  static values = {
    initialCount: Number
  }

  initialCountValueChanged() {
    this.counters.forEach(counter => {
      counter.innerHTML = this.initialCountValue
    })
  }

  get counters() {
    return Array.from(document.querySelectorAll(".counter"))
  }
}

and

<div id="update_counters" data-controller="update-counters" data-update-counters-initial-count-value="2" hidden></div>

<div class="counter">0</div>
<div class="counter">0</div>

May it help you.

1 Like

Thanks for the effort put into this. :+1:

Sorry to say, I think for now way way easier to just repeat the Stream or not repeat the element.

I think it’s a good idea to post this as a feature request to the Turbo team.

Yes, sounds like it needs to be a feature request to the Turbo team.

1 Like