Skip to content

Conversation

@timfjord
Copy link
Contributor

This PR adds assign_lazy/3 to Phoenix.Socket (if you like the idea, I will add a similar change to the Phoenix.Component module in the phoenix_live_view).

It is quite often that you need to update the socket based on the value that it is already in assigns.
For more complicated things, this could be handled with private functions, but this assign_lazy/3 is useful when you need something very simple.

It is similar to the assign_new/3, but it assigns the value even if the given key exists.

socket
|> assign_strcut1()
|> assign_struct2()
|> assign_lazy(:title, %{struct1: struct1, struct2: struct2} -> "#{strcut1.name} #{strcut2.name}") end)

@timfjord timfjord changed the title Add Phoenix.Socket.assign_lazy/3 Add Phoenix.Socket.assign_lazy/3 Nov 10, 2025
@SteffenDE SteffenDE requested a review from josevalim November 10, 2025 17:53
@josevalim
Copy link
Member

Thanks for the PR! I am not sure if _lazy is the correct name, as the assignment always happens. _lazy in Elixir only happens if there is no entry.

I feel this is one of the cases where it is probably better to break the pipeline apart (or, as you said, use custom functions).

@timfjord
Copy link
Contributor Author

timfjord commented Nov 10, 2025

Thanks for the feedback, @josevalim.
What if we handle this a bit differently?
We could add another clause for the assign/2 and accept a function as the second argument

def assign(%Socket{} = socket, fun) when is_function(fun, 1) do
  Map.update!(socket, :assigns, &Map.merge(&1, Map.new(fun.(&1))))
end

This way, the example from the description will look like

socket
|> assign_strcut1()
|> assign_struct2()
|> assign(%{struct1: struct1, struct2: struct2} -> [title: "#{strcut1.name} #{strcut2.name}")] end)

@josevalim
Copy link
Member

That would work, yes!

@timfjord
Copy link
Contributor Author

@josevalim Done ✅

I've slightly adjusted the implementation, but it has a potential issue.
Because I use recursion (which is great because I don't need to duplicate the assigns merging logic), if the callback returns a single arity function there would be an infinite loop instead of the ArgumentError (if I went with this implementation Map.update!(socket, :assigns, &Map.merge(&1, Map.new(fun.(&1)))))

But it looks shorter and cleaner.

@timfjord timfjord changed the title Add Phoenix.Socket.assign_lazy/3 Accept function in Phoenix.Socket.assign/2 Nov 11, 2025
@SteffenDE SteffenDE merged commit 6978284 into phoenixframework:main Nov 13, 2025
6 checks passed
@SteffenDE
Copy link
Contributor

🙌🏻

@timfjord timfjord deleted the assign-lazy branch November 13, 2025 13:59
zekedou added a commit to combo-lab/combo that referenced this pull request Nov 14, 2025
SteffenDE added a commit that referenced this pull request Nov 20, 2025
SteffenDE added a commit that referenced this pull request Nov 21, 2025
filipecabaco pushed a commit to supabase/phoenix that referenced this pull request Nov 26, 2025
* Add Phoenix.Socket.assign_lazy/3

* Refactoring
filipecabaco pushed a commit to supabase/phoenix that referenced this pull request Nov 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants