Robin Johnson
Robin Johnson

Reputation: 309

Enum over Elixir data, including nesting, and display the keys / vals in EEx?

I have a Phoenix app in which I need to display User's profiles in EEx / HTML, but each User's profile has varying fields, including nested data.

This would be simple to do if every user's profile had the same fields, as I could just print them straight into the EEx, but as every user has a different profile, I can't match fields.

I'm looking for the best way to loop over the User data, including the nested attributes and display the Keys/Values in EEx, line by line.

The User data looks like this:

[closed: :null, created: "2015-10-10T00:51:11.611Z",
 email: "[email protected]",
 id: "user-1234", name: "Rbin",
 profile: %{"something" => 2,
   "laptop" => %{"age" => 2, "price" => "High", "size" => "13",
     "type" => "Macbook", "working" => true}, "silly" => "properties"},
 sessions: %{"type" => "list",
   "url" => "/user-1234/sessions"}, type: "user",
 url: "/users/user-1234", username: "rbin"]

Listing multiple users was easy, as I could do a list comprehension and use a for users <- users do. I'm pretty sure I can't use that in this scenario though.

Upvotes: 5

Views: 2301

Answers (3)

shotleybuilder
shotleybuilder

Reputation: 123

My nested maps were in nested lists and so I've had to test for and pass through the lists. This is a great post for understanding the view/template process of Phoenix.

  def dump_nested(input, fun) when is_list(input) do
    Enum.map(input, fn el -> __MODULE__.dump_nested(el, fun) end)
  end

  def dump_nested(%{} = attributes, fun) do
    fun.(attributes, fun)
  end

  def dump_nested(value, _fun) do
    value
  endcode here

Upvotes: 1

Zubair Nabi
Zubair Nabi

Reputation: 1056

defmodule Nested do

def get_inner_element(input) do
Enum.at(input,0) 
|> __MODULE__.get_map(["config","accessConfigs"] )  # we can pass list here for deep nested
|>Enum.at(0) 
|> __MODULE__.get_element_from_map("natIP")
end
def get_map(map,[head|tail])  do
 map[head] |> get_map tail 
end

def get_map(map,[]),  do: map 

def get_element_from_map(map,key) do
map[key]
end
end

e.g.

input = [%{"config" => %{"accessConfigs" => [%{"kind" => 
"compute#accessConfig","name" => "External NAT", "natIP" => "146.148.23.208",
"type" => "ONE_TO_ONE_NAT"}]}}]

IO.inspect Nested.get_inner_element(input)

Upvotes: 0

Patrick Oscity
Patrick Oscity

Reputation: 54734

You need to iterate over the users recursively. You can create a function in the view module for this, which will enable you to make the recursive call inside your template. It will apply the template if it gets a map, and just return the value otherwise (this ends the recursive call).

# web/views/user_view.ex

def dump_nested(%{} = attributes, fun) do
  fun.(attributes, fun)
end

def dump_nested(value, _fun) do
  value
end

Then, in your template make sure you call the dump_nested function again for the value, since this might contain a nested map. Note that you need to pass the fun argument down as you make recursive calls so that the dump_nested function can still reference the template.

<%= dump_nested user, fn(attributes, fun) -> %>
  <dl>
    <%= for {key, value} <- attributes do %>
      <dt><%= key %></dt>
      <dd><%= dump_nested value, fun %><dd>
    <% end %>
  </dl>
<% end %>

Upvotes: 5

Related Questions