Conditional display of an HTML element part of a stream

Hi!
I face with an issue that I don’t know how to solve or make some workaround.

I have a helper that I mount to assign a :view_mode, that contains a string, generally with one of the next two values: “admin” and “dev”. Then I have a button that changes this assign and works nice. In this sense, I can have the next in my HTML:

<div :if={@view_mode == "dev"}>
  Test
</div>

And the div is just shown if @view_mode is equal to “dev”. But I have an stream in which I try to use this mechanic but it doesn’t works. The HTML for the stream is like the next one:

  <tbody id="items_stream" phx-update="stream">
    <tr :for={{dom_id, {_id, item}} <- @streams.items} id={dom_id}>
      <td>...</td>
      <td>...</td>
    </tr>
  </tbody>

If I use :if={@view_mode == "dev"} in any HTML element inside the “stream” (like in the example below), it doesn’t work as expected, nor hide nor display something already hidden. I also try to apply hidden Tailwind’s class with a conditional inside class=, but that doesn’t work too.

  <tbody id="items_stream" phx-update="stream">
    <tr :for={{dom_id, {_id, item}} <- @streams.items} id={dom_id}>
      <td :if={@view_mode == "dev"}>...</td>
      <td>...</td>
    </tr>
  </tbody>

I understand it may be related with the fact of being a stream and the use of phx-upadte="stream", does it has sense for you?

I am still thinking in a possible workaround for this issue I am having, but maybe someone of the community already knows how to face it!

Thanks in advance!

P.S. When I say it doesn’t works is because I use the same method outside the stream and inside it, outside works but inside not works.

Yup, that’s the expected behavior.

Regarding workarounds, you can create client side hooks to show/hide elements nested within a stream e.g. based on an element’s id/class attribute and view mode.

1 Like

Thank you, @codeanpeace! Solved as you suggest, using a client side hook. If I refine a bit more what I am doing I will share it :heart: For now I share here client side hook, where the magic happens:

const StreamViewMode = {
    mounted() {
        view_mode_button = document.getElementById("view_mode_button")
        view_mode_items = Array.from(document.querySelectorAll("[view_mode=dev]"))
    },
    updated() {
        view_mode = view_mode_button.attributes["phx-value-current-view-mode"].value
        if (view_mode == "admin") {
            view_mode_items.map(row => row.classList.add("hidden"))
        } else {
            view_mode_items.map(row => row.classList.remove("hidden"))
        }
    }
}

I am sure it could be improved easily, but at this moment, this works for my case!

Nice – glad you got it working!

Funnily enough, it just hit me that you don’t even need a client side hook since you can show/hide/toggle visibility with JS Commands even within streams.

You can tag the streamed elements you want to show/hide via id, class, or data attributes and then use some input e.g. button to show/hide them via
phx-click={JS.show/hide/toggle(to: "id/class/attribute dom selector")}

2 Likes

Oh, that’s true! phx-click in combination with JS really simplifies it.

Thank you for this suggestion!

1 Like