Can I access the @socket with assigns in a live component?

I have a helper function in my livecomponent that relies on socket state. What’s the best way to load the socket from the template and pass it into the helper function? When I use @socket, it doesn’t have the assigns just the Phoenix.LiveView.Socket.AssignsNotInSocket.

Helper function:

def container_id(%{assigns: %{base_id: base_id}}), do: "#{base_id}-container"

Template:

<div id={container_id(@socket)}>
</div>

Your want to send the entire socket just to get one assign? Pass that assign to the component like you normally would.

In all HEEx/EEx templates, you have a variable named assigns that contains all your… assigns :sweat_smile:.

A solution could be to just pass your container_id/1 function that assigns variable instead of @socket and modify it accordingly, e.g. :

def container_id(%{base_id: base_id}), do: "#{base_id}-container"

And in the template :

<div id={container_id(assigns)}>
...
</div>
1 Like

It’d be better to do something like this:

def container_id(base_id) when is_integer(base_id), do: "#{base_id}-container"
def container_id(_), do: "unbased-container"

And in the template :

<div id={container_id(@base_id)}>
...
</div>

This is because (via Assigns and HEEx templates — Phoenix LiveView v0.20.2):

Generally speaking, avoid accessing variables inside LiveViews, as code that access variables is always executed on every render. This also applies to the assigns variable

1 Like

Thanks, I like this version. I wasn’t necessarily trying to pass the whole socket. Just thought it might be better style at first.

You mention it’s not good to access variables inside of the liveview, yet you call container_id(@base_id). Is this call represented differently somehow?

The difference is related to change tracking, when you have the code below and pattern match on the base_id within the container_id function, it will be re-evaluated everytime the assigns changes.

def container_id(%{base_id: base_id}), do: "#{base_id}-container"

<div id={container_id(assigns)}></div>

If you follow his suggestion and use the code below instead, the function will be re-evaluated only when base_id changes.

def container_id(base_id) when is_integer(base_id), do: "#{base_id}-container"

<div id={container_id(@base_id)}></div>
2 Likes

Mmh.
It hadn’t occurred to me that it was a bad suggestion indeed, thanks for correcting it :+1:

I might have done such things in the past, in a vein attempt to “encapsulate”/abstract from the name of the variable I was using to compute such an id… And that always turned out to be quite pointless.

Another solution may also be to simply have container_id be an assign of the socket, after all.