Targeting a separate LiveView started with live_render

Hello,

I need to create a “cart” live view in the root template, I’ve added something like this in root.html.leex:

<%= live_render(@conn, CartLive.Cart, id: "cart") %>

So a new live view is started with it’s own process.

The idea is to add elements to the cart from other views, for example from products_live/products.html.leex . So I tried:

<div phx-click="add-to-shopping-list" phx-value-product="<%= product.name %>" phx-target="cart">

But it doesn’t work.

My initial guess is that phx-target is just for components, is that correct?

A workaround could be:

  1. Handle the event in each liveview page and broadcast it using PubSub
  2. Subscribe to PubSub in CartLive.Cart

I haven’t tried that, but I guess that it could work. The problem is that I’m not sure is the best way to do it.

Any other idea?

Thanks!

Hi @adrian,

Yes - I think you are correct, except I wouldn’t see PubSub as being a workaround. If you have a separate process acting as the cart, your “sending” liveview can post an update and the cart process can then broadcast an update for the cart liveview to consume. Having a non-liveview backing process for the cart will make page navigation a lot more reliable - if the user types in a URL to get to a different product page ( does a ctrl-F5 or whatever), the cart won’t go away whereas if cart state is only held in the cart liveview it would be very easy to lose it.

The initial wire-up can be a bit fiddly if you haven’t done it before (particularly figuring out how to have a separate cart backing process for each user session) but there’s plenty of docs out there.

2 Likes

Thanks a lot @mindok!

I didn’t think of having a separate process acting as the cart. I guess I’d need a genserver to store all user’s carts right? So the process would be:

user adds to cart -> genserver -> broadcast -> update live view cart

when you say that should be backed, do you mean to use any kind of persistency like PostgreSQL in the genserver too? or only the genserver would be enough?

Users are not authenticated, so I guess I should store the carts based on the session. Another question that comes to my mind is if it’s better one genserver for all the users, being the session the key to find the products in the cart, or one genserver per user.

I will have a look! Does any of the sources of the docs come to your mind?

Thanks for your help!

pushEventTo in a hook might work for this.

https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#module-js-interop-and-client-controlled-dom

1 Like

I’d go for one GenServer per cart - much like the “RoundServer” here: https://www.theerlangelist.com/article/spawn_or_not - don’t worry about spinning up lots of processes.

You can use a process registry keyed by session id to access the cart process - use of a process registry is covered in the sample code from the referenced article.

That’s what I meant by process-backed. The persistency to something like PostgresSQL is up to you - does it matter if the cart disappears if your server has to restart?

1 Like

Hi @mindok & @henrik, thanks a lot for your help here.

I’ve spent last couple of days trying, and I finally did it using @mindok approach.

I’ve created a dynamic supervisor and a Registry, and I spawn supervised Agents for user adding products to the cart.

For the communication between LiveViews (ProductsLiveView -> CartLiveView), I use PubSub, which works great

I still need to do some improvements. I don’t know how to get the session key from the cookie to live view. I’ve tried using a plug reading the cookie and use put_session but I get a cookie overflow after some reloads. Still working on that, but thanks to your help I’m very satisfied on how it works and how amazing Elixir processes are :slight_smile:

2 Likes