HEEx is Eating My Markup?

Weird problem that I don’t really understand here:

I’m working on a fairly complex form that’s manipulating a lot of associations all at once.

<.form ...>
   ...
   <%= for f <- inputs_for(f, :entries) do %> <!-- Has many -->
      ...
      <%= for f <- inputs_for(f, :quiz) do %> <!-- Has one -->
         ...
         <%= for f <- inputs_for(f, :questions) do %> <!-- Has many -->
            <p>This content doesn't render</p>
         <% end %>
      <% end %>
   <% end %>
</.form>

Nothing inside the third for renders for some reason. I’ve put <% IO.inspect("rendering") %> inside the for loop and confirmed it is looping.

I’ve also confirmed that the contents of the loop are not being sent over the websocket. The diff message doesn’t include anything where the loop should be generating content.

I’ve also wrapped an IO.inspect around the whole loop to see what’s being generated:

<%= IO.inspect(for f <- inputs_for(f, :questions) do %>
   ...
<% end) %>

I can see the html markup list stuff that the loop should be returning.

What’s weird is that when I add the IO.inspect, the markup does get sent to the browser and rendered correctly!

All I can think is that HEEx/LiveView has some limit to the level of nested looping it can handle, and wrapping it up in a function call bypass some change tracking code or something like that? Three loops seems like a really low limit though.

1 Like

Are you sure you have <%= and not <% with that block?

1 Like

Yep. That was the first thing I checked. Just double-checked too.

1 Like

Are you sure there’s actually assocs to render? Because if not inputs_for(f, …) will give you an empty list and therefore not render anything.

1 Like

Yes, I checked that too:

<%= for f <- IO.inspect(inputs_for(f, :questions)) do %>` 

and

<%= IO.inspect(for f <- inputs_for(f, :questions) do %>
<% end) %>

both log the expected data.

1 Like

Shouldn’t this be?!

   <%= for entryform <- inputs_for(f, :entries) do %> <!-- Has many -->
      ...
      <%= for quizform <- inputs_for(entryform, :quiz) do %> <!-- Has one -->
1 Like

Do you mean the duplicate f binding? That shouldn’t make a difference - the inner f shadows the outer f.

I just tried it to make sure and it makes no difference.

Yes, it is shadowed… :slight_smile:

Given this it might be a bug in change tracking. Doing the IO.inspect should essentially prevent (granular) change tracking.

It looks like a bug. There is no such things as looping limit. Make sure you are on latest LiveView and then try isolate the bug. For example, can you reproduce it with this:

<%= for f <- [1, 2, 3] do %>
  ...
  <%= for f <- List.wrap(f) do %>
    ...

And so on?

If you can isolate it, then please open up a bug report. :slight_smile:

1 Like

I had this once. Adding inspect made the code work.
Turned out I tripped over a pitfall.

1 Like