LiveView - Passing data between multiple LiveViews

I am building a simple ecommerce site. I have decided to give LiveView a try; this is my first project using the technology. I have run into a snug that I don’t know how to solve:

I have the following LiveViews:

  • One for product searching and listing - with its own URL like /products?search=laptop
  • One for product detail view - with its own URL like /product/spanky-and-powerfull-laptop
  • One for checkout - with its own URL like /checkout
    Currently, they exist in separation.

I would like to persist the cart content of an anonymous user somewhere. I have reached for the session mechanism (cookie-based) for this task.

As there is no easy way to write into the session when inside a LiveView, I am using a mechanism where a Javascript LiveView hook responds to a LiveView event (sent by the server) by invoking a purpose-made “session controller” via a POST request to write into the session. Although I am not entirely keen on this, I can’t see a different way of achieving what I need, and it works. Where this approach falls apart, however, is when transioning from one LiveView (say the Product Search) to another one (the Product Detail) by doing a live redirect. The newly spawned LiveView does not get to see the updated session data, I guess understandably - as the whole page does not go through the whole Phoenix pipeline.

More generally, I guess this question is about how to pass data from (an existing) LiveView to (a newly spawned) LiveView, without storing the data in the URL.

I am thinking that organising the LiveViews in some kind of hierarchy might be one way to communicate data between LiveViews. Is that right? Are there other ways?

Cheers!

You could cache the data by using ETS or https://github.com/whitfin/cachex (which I believe uses ETS under the hood) and quickly access it wherever you like.

2 Likes

Thanks for the suggestion.

To describe the mechanics of the suggested solution as I understand it, you are suggesting that:

  • A unique ID is stored in a new session when it’s created.
  • Using the unique session ID to create, write to, and read from a record in ETS / cachex from within the liveviews?

Is this correct?

I think that’s exactly what @pedromtavares meant. There’s another approach to solve your problem and I guess it might be inferior to using ETS in terms of efficiency but it’s FYI. You might use a Registry and a DynamicSupervisor. For each connected client, you spawn a process under the DynamicSupervisor and register it via the Registry under the process’ unique id. Your LiveViews communicate with the process. After a client disconnects you might schedule a :normal exit of the process. About the state, you might persist it somewhere after you’ve processed the clients’ messages and keep a working state in the process’ state. When the client has been away for a long time and visits your page, you can spawn his dedicated process with the persisted state.

3 Likes

Thanks again for suggesting an alternative solution.

I’d like to come to a solution that is as simple as possible and with the least amount of server-side “components”.

I am quite happy with the persistence of the data in the session - in a cookie: It’s on the client and I don’t need to worry about keeping it in some sort of server-side storage: a cache solution, ETS, server memory, or a database.
Keeping the state server-side and outside of the LiveView process would seem to add another level of complexity, particularly in terms of house-keeping of the records.

Do you think there is another way that would allow various LiveViews that are related to one session to have some kind of R/W access to the same data without introducing other, non-LiveView “components”? Something like a Svelte / React context?

Cheers!

I get the impression you’re looking for something like pub/sub. The easiest and at the same time scalable way to get pub/sub is to use a Registry. You can read more here. Be careful to not send a message to the process which is publishing.

Disagree. Registry pubsubs are IMO difficult to wrangle and not distribution-friendly without extra work. If you have Phoenix liveview already, you should use Phoenix pubsub since it already ships, the syntax dead simple, and it’s distribution friendly out of the box (possibly with literally “nothing to do” if you have a erlangs default PG2 cluster)

Of course they’re not distribution-friendly, they’re local pubsubs, it’s even written in the docs. OP hasn’t mentioned he needs distributed solution.

Thank you.
In this case, when you have an existing LiveView that you navigate away from using live_redirect, which shuts down the existing one and causes a new LiveView to be mounted, who do you envisage that the publishing and the subscribing parties would be?

In essence, all I would like to achieve is to pass some extra data (cart content, in my case) from an outgoing LiveView to a new one, without using the URL as the conduit. Currently, I can’t find if there is a simple way to achieve this without reaching out to some extra server-maintained state, and I am wondering if I am not seeing something obvious…

Cheers!

1 Like

I am currently struggling with the same question: how to pass data from one liveview to another without storing it in the URL. I have found all kind of suggestions like ETS, GenServer, but everything seems a bit complicated for what I want to accomplish: just pass a parameter to new liveview.

Did you find a solution in the mean time?

It has been discussed recently, such as here:

To pass data from the client side you can:

  1. use query string in url
  2. use localStorage and a hook