The image below is an app that I am working on and attempting to add a feature to.
Quick description of initial code
The items that are stacked on top of one another are called “testbeds” and these are pulled from Postgres via Ecto. They are then assigned to the socket, rendered and iterated through. TestBeds uses a pub-sub feature so when changes are made to this collection the changes are broadcast to other users.
def mount(_params, _session, socket) do
TestBeds.subscribe()
{:ok,
assign(socket,
testbeds: TestBeds.list_testbeds(), #Get Testbeds
# code ...
)}
end
Render
def render(assigns) do
~H"""
<body>
<div>
<%= for testbed <- Enum.filter(@testbeds, fn(item)-> item.group_id != nil end)|>Enum.sort_by(&("# {&1.group.name}#{&1.name}"), :asc) do %>
<%!-- code.... --%>
<% end %>
</div>
<%!-- code ..... --%>
</body>
"""
end
In the code above the testbeds are explicitly sorted by a property named group.name. The reason I am sorting it like this is because if I iterate and render testbeds with no sorting, the testbeds will automatically sort by the update_at property (and this change will be broadcast to other users) and this is not what I want. Choosing to sort it by group.name is arbritary, I could have used any property.
What is the problem?
You see those headers at the top? I want the user to be able to click each one and as a result the testbeds automatically sort by that columns data.
So far I have it partially working. I updated the testbeds with the previous sorting removed and so the code now looks like this:
<%= for testbed <- @testbeds do %>
<%!-- code.... --%>
<%-- Example of sorting applied to a heading. The actual code has about 12 of these --%>
<h2 class="info-button" phx-click="sort_by_string" phx-value-field="hardware"> Hardware</h2>
<%!-- code.... --%>
<% end %>
The code to sort the testbeds looks like this:
def handle_event("sort_by_string", %{"field" => field}, socket) do
atomParam = String.to_existing_atom(field)
IO.inspect atomParam
if socket.assigns[:sort_by_name_ascending] == false do
sorted_testbeds = Enum.sort_by(socket.assigns.testbeds, fn item -> Map.get(item, atomParam) end) #hardware is hard coded as an atom
{:noreply, assign(socket, testbeds: sorted_testbeds, sort_by_name_ascending: true)}
else
sorted_testbeds = Enum.sort_by(socket.assigns.testbeds, fn item -> Map.get(item, atomParam) end) |> Enum.reverse() #hardware is hard coded as an atom
dbg(sorted_testbeds)
{:noreply, assign(socket, testbeds: sorted_testbeds, sort_by_name_ascending: false)}
end
end
The Problem
The field titled Set Status (see image) has a button that lets the user make an update to a property of testbeds named status (in the image this is the button displaying Available or Taken). When this happens, the testbed order changes and re-orders based on the values in update_at property. I do not want this. I want the order of the testbeds to stay as-is and not to be broadcast to users. However, I do want the status update (the change from Available to Taken) to be broadcast. To be clear, when a user changes the status no sorting should be visibly made or broadcast but the status value change should be broadcast. The status values are Available and Taken (see image).
To make this worse, as mentioned, I have integrated the pub-sub feature so that when a user changes the status of a testbed, not only does the status change but the ordering is broadcast to everyone. I want the ordering to visibly stay as-is for any user that changes a testbed status.
The status code is here:
def handle_event("create-status", params, socket) do
# Create Record
%{"status" => status, "testbed_id" => testbed_id, "developer" => developer} = params
stripped_developer = String.trim(developer)
if stripped_developer === "" || String.trim(developer) === "" || String.trim(developer) === "None" do
{:noreply, assign(socket, developer: developer, name_warning: " (can't be empty or set to None)")}
else
TestBeds.get_test_bed!(testbed_id)
|> TestBeds.update_test_bed(%{status: status, developer: developer})
current_testbed = TestBeds.get_test_bed!(testbed_id)
StatusActions.create_status_action(%{
testbed_name: current_testbed.name,
testbed_value: testbed_id,
status: status,
developer: developer
})
{:noreply, assign(socket, developer: developer)}
end
end
The reset (Changes **Taken** back to **Available**) is here:
def handle_event("reset", %{"id" => id}, socket) do
testbed = TestBeds.get_test_bed!(id)
StatusActions.create_status_action(%{
testbed_name: testbed.name,
testbed_value: id,
status: "Available",
developer: testbed.developer
})
TestBeds.get_test_bed!(id)
|> TestBeds.update_test_bed(%{status: "Available", developer: "None"})
# {:noreply, socket}
{:noreply, assign(socket, testbeds: TestBeds.list_testbeds())}
end
Summary
When a user sorts data by clicking the headings. the testbeds should sort by column data and this sorting should not be broadcasted to other users. This currently works with one exception.
The exception
When a user changes the Status value sorting takes and is broadcast to other users. This should not happen.