Cleaner push_patch and handle_params?

Hey all!

So I’ve been creating a liveview with a lot of closed “boxes” that can be opened and closed with buttons, and the states get saved in the url via push_patch.

My solution works but it is a lot of hardcoded stuffs which I want to clean up, but I’m really unsure how!

Currently there are like 10 boxes, but for the sake of example I will only type out 2 boxes, but pretend that its 5 times as much code… Help


So each box has a open and close button like this

<button aria-label="Minimize" phx-click="box_blue" phx-value-state="true"></button>
<button aria-label="Maximize" phx-click="box_blue" phx-value-state="false"></button>

<button aria-label="Minimize" phx-click="box_red" phx-value-state="true"></button>
<button aria-label="Maximize" phx-click="box_red" phx-value-state="false"></button>

I catch the button events like this (This function is the worst… Lines with all 10 boxes assigns hardcoded on each case…)

def handle_event(window, %{"state" => state}, socket) do
    case window do
        "box_red" ->
            {:noreply, push_patch(socket, to: Routes.page_path(socket, :index, box_red: (if (state == "true"), do: false, else: true), box_blue: socket.assigns.box_blue))}
        "box_blue" ->
            {:noreply, push_patch(socket, to: Routes.page_path(socket, :index, box_blue: (if (state == "true"), do: false, else: true), box_red: socket.assigns.box_red))}
    end
end

Because my code adds all 10 states to the url if a single box is open, I have to catch when all boxes are “false” to clean up the url like this:

def handle_params(%{"box_red" => "false", "box_blue" => "false"}, _, socket) do
    socket =
      socket
      |> assign(box_red: false, box_blue: false)
    {:noreply, push_patch(socket, to: Routes.page_path(socket, :index))}
end

And because I lazy load the boxes, I catch the params like this

def handle_params(params, _, socket) do
    red_box = params["red_box"] || "false"
    blue_box = params["blue_box"] || "false"

    if (red_box == "true" and not socket.assigns.red_box_loaded) do
        Process.send_after(self(), %{event: "load_box", payload: "red_box"}, 50)
    end

    if (blue_box == "true" and not socket.assigns.blue_box_loaded) do
        Process.send_after(self(), %{event: "load_box", payload: "blue_box"}, 50)
    end

   {:noreply, assign(socket, 
      blue_box: blue_box, 
      red_box: red_box
    )}
end

And finally, I load the boxes like this

def handle_info(%{event: "load_box", payload: payload}, socket) do
    case payload do
        "red_box" ->
            data = Repo.load_some_data("red_box")
            socket = assign(socket, :red_box_loaded, true)
            {:noreply, push_event(socket, "init_red_box", %{data: data})} # JS Interop
        "blue_box" ->
            data = Repo.load_some_data("blue_box")
            socket = assign(socket, :blue_box_loaded, true)
            {:noreply, push_event(socket, "init_blue_box", %{data: data})} # JS Interop
    end
end

So I want the URL to not be populated with red_box=false, only when they are true. And I really don’t want to type all this hardcoded jam when implementing new boxes…

Help much appreciated, sincerely: my sanity

Ok, to narrow down my problem, I guess I’m looking for a way to do this line but only with boxes that are true (i dont want them set to false in the url), and without hardcoding each assign…

{:noreply, push_patch(socket, to: Routes.page_path(socket, :index, 
    box_red: (if (state == "true"), do: false, else: true), 
    box_blue: socket.assigns.box_blue, 
    box_yellow: socket.assigns.box_yellow, 
    box_green: socket.assigns.box_green
))}