Reputation: 175
I am attempting to do something like the below in my eex template:
<% current_production_date = nil %>
<%= for widget <- @widgets do %>
<%= if (current_production_date != widget.production_date) do %>
<!-- ... output a new date header and re-assign current production_date -->
<% current_production_date = widget.production_date %>
<% end %>
<%= render "_widget.html", widget: widget %>
<% end %>
This won't work as the outer "current_production_date" variable can't be re-assigned inside the comprehension. This seems like a common scenario thou, so I imagine there is a straight-forward way of accomplishing this... I just can't figure it out... any hints much appreciated!
Upvotes: 0
Views: 200
Reputation: 175
Thanks for the suggestions, I ended up going with a suggestion I got from the Elixir forum... using group_by
:
<%= for {date, widgets}
<- Enum.group_by(@widgets, fn(x) -> DateTime.to_date(x.production_date) end)
|> Enum.sort(fn({date1, _widget1}, {date2, _widget2}) ->
case Date.compare(date2, date1) do
:lt -> true
_ -> false
end
end) do %>
<%= render "_date_header.html", date: date %>
<%= for widget <- widgets do %>
<%= render "_widget.html", widget: widget %>
<% end %>
<% end %>
I ended up extracting this into the view as it's a little nasty when it's directly in the template.
Upvotes: 0
Reputation: 121010
While the @Badu’s answer is technically correct, it’s [opinionated] not fully idiomatic Elixir since it has code duplication and uses [opinionated] the wrong abstraction to present chunks of data.
What you do have is literally a list of chunks, so what you probably need is Enum.chunk_while/4
.
chunk_fun = fn
widget, [] ->
{:cont, [widget]}
# ⇓⇓ ⇓⇓ PATTERN MATCH!
%{production_date: pd} = widget, [%{production_date: pd} | _] = prev ->
{:cont, [widget | prev]}
widget, acc ->
{:cont, Enum.reverse(acc), []}
end
after_fun = fn
[] -> {:cont, []}
acc -> {:cont, Enum.reverse(acc), []}
end
widgets = Enum.chunk_while(@widgets, [], chunk_fun, after_fun)
Now in widgets
you have chunks of @widgets
, grouped by date. Let’s output them:
for [%{production_date: date} | _] = chunk <- widgets do
# output the header with the date
for widget <- chunk do
# render the widget
end
end
I did not test this code but it should work as it is.
Upvotes: 1
Reputation: 1082
You can use Enum.reduce/3
to accumulate the result and output the result after.
<%
current_production_date = nil
{result, _} =
Enum.reduce(@widgets, {[], current_production_date},
fn %{production_date: production_date} = widget, {acc, current_date} ->
if product_date != current_date do
output = "<h1>output a new date header and re-assign current production_date</h1>"
{[output, Phoenix.View.render_to_string(PageView, "widget.html", widget: widget)
|acc], production_date}
else
{[Phoenix.View.render_to_string(PageView, "widget.html", widget: widget) |acc], current_date}
end
end) %>
<%= for w <- Enum.reverse(result) do %>
<%= raw(w) %>
<% end %>
Upvotes: 0