I’m trying to dynamically render some function components for my navigation menu and am looking for a way to potentially do it a little… nicer?
What I have right now is:
# in module OutlineIcons
def home(assigns) do
assigns = assign_defaults(assigns)
~H"""
<svg id={@id} class={@class} style={@style} xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
"""
end
# in module Page
def page(assigns) do
navigation = [
{"Home", Routes.home_index_path(assigns.socket, :index), &OutlineIcons.home/1}
]
# add it to assigns, lots of markup...
~H"""
<%= for {label, href, icon} <- @navigation do %>
<%= live_redirect to: href, class: "text-gray-600 hover:bg-gray-50 group flex items-center px-3 py-2 text-sm font-medium rounded-md" do %>
<%= icon.(%{__changed__: %{}, class: "text-gray-400 group-hover:text-gray-500 flex-shrink-0 -ml-1 mr-3 h-6 w-6"}) %>
<%= label %>
<% end %>
<% end %>
"""
end
Now, since my icon component has an assigns map as parameter, I need to create my own one by adding %{__changed__: %{}. This works and renders just fine, but seems verbose. Is there a nicer way to do this?
After having read more about function capturing (Modules and functions - The Elixir programming language), I changed my approach. Just like OP, I now pass captured functions to my navigator component (&home_icon/1), instead of function name strings (“home_icon”).
I was sort of hoping maybe something like <.{icon} color="red"/> would work to call a function component dynamically, but the component macro gets the job done.