Eex: show date only when new

Hello,

So far I’m pretty surprised about my progress in deloping my first simple phoenix app. But today I came to an halt.

I have a database table events. Each event has a name and a happens_at date field. At any date there can be 0 to n events.

I want to list those events on a page (events.html.eex) ordered by date, which is simple and gives me an output like this:


22th October 10:00: Event aa
22th October 12:30: Event dd
22th October 15:00: Event cc
22th October 17:30: Event gg


23th October 09:00: Event bb
23th October 12:00: Event zz


But what I really want that to look like is rather:


22th October:


10:00: Event aa
12:30: Event dd
15:00: Event cc
17:30: Event gg


23th October:


09:00: Event bb
12:00: Event zz

So my question is: How can I achieve that the date is only displayed once for a day?

My first attempt was to use a “last_date” variable that is compared to the event’s date while going through the events list and get’s shown (and then updated) only if the event’s date is newer. That didn’t worked because… because I cannot assign last_date a new date in Elixir as it’s immutable. I should have known…
Do I need recursion?

BTW: Is it better to use the template and put some embedded elixir in or define it via viewer? Or is it just a matter of taste?

Thanks in advance.

Are you just wanting to separate by date? What about just using Enum.group_by/* then sort via NaiveDateTime.compare or so? :slight_smile:

@Nefcairon: You can do it at database level:

defmodule MyApp.Example do
  @moduledoc "Example context"

  alias Ecto.Query
  alias MyApp.Example.Event
  alias MyApp.Repo

  def get_events_grouped_by_date do
    Event
    |> Query.group_by([event], fragment("date(?)", event.happens_at))
    |> Query.select(
      [event],
      fragment(
        "json_build_object(date(?), json_agg(json_build_object('name', ?, 'time', ?::time)))",
        event.happens_at, event.name, event.happens_at
      )
    )
    |> Repo.all
  end
end

Using such function you can use it like:

<%= for {date, list} <- @data do %>
  <span>
    <%= date %>
  </span>
  <ul>
    <%= for %{"name" => name, "time" => time} <- list do %>
      <li>
        <span>
          <%= time %>: Event <%= name %>
        </span>
      </li>
    <% end %>
  </ul>
<% end %> 
3 Likes

There is one bad in my code (sorry for that). get_events_grouped_by_date is returning list of maps (not single map), so it should be:

defmodule MyApp.Example do
  @moduledoc "Example context"

  alias Ecto.Query
  alias MyApp.Example.Event
  alias MyApp.Repo

  def get_events_grouped_by_date do
    Event
    |> Query.group_by([event], fragment("date(?)", event.happens_at))
    |> Query.select([event], %{
      date: fragment("date(?)", event.happens_at),
      list: fragment(
        "json_agg(json_build_object('name', ?, 'time', ?::time))",
        event.name, event.happens_at
      )
    })
    |> Query.subquery()
    |> Query.from()
    |> Query.select([query], fragment("json_object_agg(?, ?)", query.date, query.list))
    |> Repo.one
  end
end
1 Like

@Eiji, @OvermindDL1: Thank you both, with your hints I have now implemented a solution. It’s surely suboptimal, but it works. Maybe I have to improve it, but I wait until it underperforms.

1 Like