I have a list view where various actions modify an initial list to be displayed. Initially I was duplicating all the logic for this filtering in every action, which worked fine but resulted in a lot of duplicated code like this:
def render(assigns) do
filtered_assigns = assigns
|> apply_some_filter
|> another_filter
MyView.render("list", assigns |> Map.put(:filtered_assigns, filtered_assigns))
end
def handle("some_action", _, socket) do
some_assigns = do_some_action
socket = socket
|> assign(:some_assigns, some_assigns)
{:no_reply, socket}
end
This appeared to work at first, insofar as @filtered_assigns was available in the template and filtered corrected, but when I tried calling some_action it no longer updated the template, even though I verified the value of the assign was updated in the render function itself. So, e.g., Enum.count(@filtered_assigns) returned 2, but the template still included 3 items.
So, instead I moved the filters to a āhelperā method in MyView instead, and everything worked perfectly with no change in the filter logic itself.
Iām fine with storing that kind of logic in the view rather than modifying assigns in render, but I didnāt see anything in the docs that suggested I shouldnāt be able to do the latter. What I am I missing? I assume the assign function does something internally that Map.put doesnāt?
In your case LiveView knows that some_assign changed, but the template reads filtered_assigns, not some_assign, so it thinks thereās no need to re-render. As you guessed the assign function also marks the field āchangedā - itās not just a Map.put. Thatās why setting filtered_assigns directly doesnāt mark that field as changed.
One pattern Iāve seen is to define a helper e.g. apply_filter that does the computation and assigns the result (filtered_assigns) to the socket, and call it every time you update a field. It works well if you need the value in multiple places in the template so you donāt recompute it every time.
I guess the thing that threw me off is that the assign being filtered did often change as a result of the actions in question. But I didnāt think about it in terms of what assigns are referenced by the template.
It does seem a bit weird to define a function that essentially only delegates to another function, and breaks if you do anything else with the variable it explicitly exposes.