Existenz

Existenz

Hello there.
I have been trying out liveview for some time, but the diff tracking is still kind of a mystery to me. I’m running into a weird issue when combining it with pub/sub.

I checked out this repo(which is not mine) which creates pretty basic comment functionality with liveview: https://github.com/snewcomer/live-comment

There is one liveview with all the comments. Each comment is a live component, possibly containing children comments. So we have recursive comment component(show.ex). When I post a child comment, assigns change in the specific component and the component is rerendered.

def handle_event(“save”, %{“comment” => comment_params}, socket) do
comment_params
|> Map.put(“parent_id”, socket.assigns.id)
|> Managed.create_comment()
|> case do
{:ok, new_comment} →
{:noreply, assign(socket, form_visible: false, children: [new_comment])}

{:error, %Ecto.Changeset{} = changeset} →
{:noreply, assign(socket, changeset: changeset)}
end
end

This works as expected.
Since we are using temporary assigns and phx-udpate append, only the new comment is send over the wire.

The trouble begins when we broadcast this new comment to all liveviews using Phoenix.PubSub.broadcast_from!
The liveview is subscribed to new_comment event and when it comes, it calls send_update with the comment component id like this:

def handle_info({Managed, :new_comment, comment}, socket) do
if comment.parent_id do
send_update(CommentLive.Show, id: comment.parent_id, children: [comment])
{:noreply, socket}
else
{:noreply, assign(socket, comments: [comment])}
end
end

Actual behaviour:
The incognito window shows that all siblings are sent over to the client. Note: not ALL comments are sent. the other root comments are not sent. only the children belonging to same parent. So if I have comments like this:

root1
    comm1
    comm2
root2
root3

and I add comm3 as a reply to root1, all children of root1 are sent to the client(comm1, comm2 and the new comm3). root2 and root3 are not sent at all.

Expected behaviour(for me)
:new_comment event is published, the liveview in incognito window is subscribed, gets the new child comment, calls send_update with the comment. Then the corresponding livecomponent updated its assigns with the new comment and only the new comment is sent as diff to the client(just as it works without pub/sub)
So from the previous example, only the comm3(without comm1 and comm2) should be sent to the client.

My question is why are all the siblings sent to the client, when I create new comment?
Is this supposed to work as I’m expecting and I’m just doing something wrong, or is this not the way it works and my understanding is wrong?

thanks

Showing Posts 1 to 10

Existenz

Existenz OP

Anyone?

I still cant make sense of it.

wanton7

wanton7

I haven’t really used LiveView yet but what I know is that it tracks elements by DOM ID from this reply How do I optimize LiveView when using pagination? - #3 by josevalim . Do you have DOM ID’s set for your elements?

Existenz

Existenz OP

Hi,
thanks for the reply. I do have DOM IDs set for every comment component.

50kudos

50kudos

Just have a quick look at the code from link you posted. It shows that preload/1 loads all comments before update/2 (default; i.e. didn’t override)

Try define update function next to preload function

def update(assigns, socket) do
IO.inspect(assigns.children, label: "assigns")
IO.inspect(socket.assigns.children, label: "socket")
end

I hope you find answer around there.

Existenz

Existenz OP

But wouldn’t liveview do the diff and send only the newest child comments even though I load all of them when I have children in temporary assigns and use phx-append?

50kudos

50kudos

Normally, yes, Diff = Current - Previous, right? The example code has temporary_assigns: [comment: nil, children: []]} so once view is rendered, and new comment is submitted, fetched_children - [] = fetched_children. (I didn’t pull the code and run debugging though that’s why you could try IO.inspect in update function there)

I’d debug both the broadcaster and the receiver. The broadcaster has children: [new_comment] on their socket, but receiver will call send_update which will call preload/1 and update/2 the first parameter (assigns) of update/2 will come from the send_update and through preload/1, and the second parameter is existing socket.assigns (at this point it should be empty [ ]), and it will merge the assigns (first parameter) into it.

Did you try debugging around the preload and update function? I won’t be sure for sure until I really pull the code down, run, and debug. Hope this helps a little more. :slight_smile:

Existenz

Existenz OP

I have not yet debugged the code around update/2 and preload/1. I will soon.

So that would mean, if I understand it correctly, that even though I’m sending only new comment through send_update(comments: [new_comment]), all of the comments are assigned to socket and sent to client, since I’m loading all of them in the preload/1 function. Right?

50kudos

50kudos

The broadcaster node is already correct (only send a new reply)
To fix the receiver node, put these new 3 lines of code. That’s it!

lib/live_comment_web/live/comment_live/show.ex

  def preload(list_of_assigns) do
    # https://hexdocs.pm/phoenix_live_view/Phoenix.LiveComponent.html#module-preloading-and-update
+   {send_update_assigns, list_of_assigns} = Enum.split_with(list_of_assigns, fn a -> Map.get(a, :prevent_preload) end)
    
+   if send_update_assigns == [] do
      parent_ids = Enum.map(list_of_assigns, & &1.id)
      children = Managed.fetch_child_comments(parent_ids)
    
      Enum.map(list_of_assigns, fn assigns ->
        Map.put(assigns, :children, Map.get(children, assigns.id, []))
      end)
+  else
+    send_update_assigns
+  end 
  end

lib/live_comment_web/live/comment_live/index.ex

  def handle_info({Managed, :new_comment, comment}, socket) do
    if comment.parent_id do
-     send_update(CommentLive.Show, id: comment.parent_id, children: [comment])
+     send_update(CommentLive.Show, id: comment.parent_id, children: [comment], prevent_preload: true)
      {:noreply, socket}
    else
      {:noreply, assign(socket, comments: [comment])}
    end 
  end 
Existenz

Existenz OP

Thank you very much, I will try that out

Existenz

Existenz OP

I just got the chance to test your solution and it totally worked. Thanks a lot

— All posts loaded —

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
water
I’m aware of 2 such projects, are there others? git: GitHub - team-alembic/realworld: A fullstack Phoenix LiveView application with back...
New
katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
peck
Hi everyone. My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
New
bradley
I really like the adapter patterns that ecto, nebulex, waffle, etc. use and would love find something similar for a key management servic...
New
unaware8150
Hello folks! So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
Alvinkariuki
How Can I Optimise Compile Time Dependencies I have been building an elixir application for about 2 years now. Many modules and files ha...
New

Other Trending Topics Top

mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New
budgie
I love Elixir. It’s one of 2 programming languages I’ve ever fallen in love with. But I don’t use it anymore. Serverless was the promis...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews