I have a question regarding printing a map inside an eex template.
How do I print out the key of a map within the template in, for example, a standard html tag such as “<h1>”? I’ve created the logic behind it and have printed it using IO.puts but I can’t seem to get the syntax correct for eex.
Here’s the code that I’m trying to implement:
for {k, v} <- gps do
for p <- patients do
if p.gp_id == k do
IO.puts(v<>":"<>p.fname<>" "<>p.lname)
end
end
end
I’ll elaborate some more. The function of the page is to act as a report page. The system holds patients, and each one has a GP associated with it. The idea is to print out a report that shows “What patients belong to what Gp” in the format:
GP 1
- Patient 1
- Patient 2
GP 2
- Patient 3
- Patient 4
The issue I’m having is displaying this data in any way inside the html template (I’m sure the data is being sent correctly)
for {k, v} <- gps do
for p <- patients do
if p.gp_id == k do
# ...
end
end
end
can maybe be done a bit more efficiently, I think
def prepare_data(gps, patients) do
patients
|> Stream.group_by(fn patient -> patient.gp_id end) # group all patients by their gp_id
|> Stream.map(fn {gp_id, patients} -> {gps[gp_id], patients} end) # get gp's data by their id
|> Enum.reject(fn {gp, _patients} -> is_nil(gp) end) # filter out any gps that are not in `gps`
end
output from prepare_data/2 can be rendered like this then
def index(conn, _params) do
gps = Repo.all(GP)
patients = Repo.all(Patient)
render conn, "patientbygp.html", patients: patients, gps: gps
end
and patientbygp.html
<div class="jumbotron">
<%= for {k, v} <- @gps do %>
<%= for p <- @patients do %>
<%= if p.gp_id == k do %> <h1><%= v %>:<%= p.fname %> <%= p.lname %><% end %></h1>
<% end %>
<% end %>
</div>
Just to clarify, I understand that the html code will not make the page look like the screenshot, at the moment I’m more concerned with getting things displaying rather than blank space.
and the extra logic in the template could probably be replaced by a bit more elaborate sql query or maybe
defmodule GP do
def list(preloads) do
GP
|> Repo.all()
|> Repo.preload(preloads)
end
end
# and then in the controller
gps_with_patients = GP.list([:patients])
The rendering part then becomes rather straightforward (as I’ve shown in my previous post).
If your for comprehensions return empty lists or lists of nil, it means that the patterns in them don’t match.
For example, <%= for {k, v} <- @gps do %> this won’t work if @gps is not a map or a list of {k, v} tuples. And Repo.all(GP) probably returns a list of %GP{}, which is neither of those, so for silently fails and returns an empty list.
Are you sure that Repo.all(GP) returns something that is similar to a list of Key-Value Pairs?
Sorry to say, but I doubt that, since usually Repo-functions do return structs, So in your case I do expect it to be a list of %GP{}-structs. Thats probably why it does not work.
Of course, what @idi527 said about the optimized query is absolutely true.
Another way to do this is with Enum.group_by/3. Assuming your Patient has a gp_id field and (in this sample) they each have a name field for illustrative purposes.
<%= for {gp_id, gp} <- Enum.group_by(gps, &(&1.id)) do %>
<h1><%= gp.name %></h1>
<ul>
<%= for patient <- Enum.filter(patients, &(&1.gp_id == gp.id) do %>
<li><%= patient.name %></li>
<% end %>
</ul>
<% end %>