Function pattern matching with concatenation in HEEX component

I have an activity feed where each item has the same general layout but different attributes are used to compute the final text. The items are rendered by a stateless function component as they are immutable (ie, an event of a thing that happened). But I leverage function pattern matching in the component to decide how to compute the text so the caller doesn’t have to know what type of activity item it has.

I recently upgrade to Phoenix LiveView 0.18.2 and I am getting the following warning:

Using variables in HEEx templates are discouraged as they disable change tracking. You are only allowed to access variables defined by Elixir control-flow structures, such as if/case/for, or those defined by the special attributes :let/:if/:for. If you are shadowing a variable defined outside of the template using a control-flow structure, you must choose a unique variable name within the template.

Instead of:

    def add(assigns) do
      result = assigns.a + assigns.b
      ~H"the result is: <%= result %>"
    end

You must do:

    def add(assigns) do
      assigns = assign(assigns, :result, assigns.a + assigns.b)
      ~H"the result is: <%= @result %>"
    end

My offending code is:

# my_sweet_function_component.ex
def render(%{event: "merge:" <> action} = assigns) do 
  ~H"""
    <%= format_action_name(action) %>
  """
end

def render(%{event: @another_event} = assigns) do
    ~H"""
       <%= format_display(@assigns.other_attribute) %>
     """
end

I understand the warning saying “hey, don’t be messing with these local variables cause we can’t keep track of them.” However, I don’t care about tracking since that data won’t change. I could rewrite how I call this function to do the extraction on the caller side, but then I lose the very handy ability to pattern match implementations in my function head.

What’s the recommended way to handle this situation?

1 Like

You still can pattern match, just cannot used matched value from your template

def render(%{event: "merge:" <> action} = assigns) do 
  assigns = assign(assigns, :action, action)
  ~H"""
    <%= format_action_name(@action) %>
  """
end

def render(%{event: @another_event} = assigns) do
  ~H"""
    <%= format_display(@other_attribute) %>
  """
end
2 Likes

after 10 minutes outside I found the obvious solution that lets me keep my setup:

# my_sweet_function_component.ex
def render(%{event: "merge:" <> action} = assigns) do 
  assigns =
    assigns
    |> assign(:action, action)

  ~H"""
    <%= format_action_name(@action) %>
  """
end

moral of the story: take a walk when you’re stuck :v:

3 Likes