How to transition from LiveView to Regular HTTP?

Hello,
Iam new to Phoenix LiveView and I wanted to try something :
Consider we have resources for something let’s say %Material{}, and we want to use a LiveView form to add new Material, the form will look like this

<.form for={@form} phx-change="validate" phx-submit="save">
  <.input type="text" field={@form[:username]} />
  <.input type="email" field={@form[:email]} />
  <button>Save</button>
</.form>

Once the form submitted, we want to create a new Material and show it by redirecting to materials/:id, the code for NO LiveView version(consider we used NO LiveView form) looks like that

def create(conn, %{"material" => material_params}) do
  case Trade.create_material(material_params) do
    {:ok, material} ->
      conn
      |> put_flash(:info, "Material created successfully.")
      |> redirect(to: ~p"/materials/#{material_params.id}")

    {:error, %Ecto.Changeset{} = changeset} ->
     .... 
  end
end

That will give a flash message and being redirected to show the new created material
The problem is with LiveView version, we can’t do that because we are in websocket with socket state instead of conn (both put_flash and redirect expect conn argument and a socket will be catched by Plug.Conn),
so what should we do in such situation ?
I asked that to get a global vision how to work by mixing LiveView with Regular HTTP requests and thank you in advance.

At a high level, Phoenix.Router will direct all requests for live routes to the specified Phoenix.LiveView and all other requests for “dead” routes to its specified Phoenix.Controller action.

It might be helpful to play around with mix phx.gen.live — Phoenix v1.7.1. The generated form livecomponent will demonstrate that LiveView exposes put_flash/3 which accepts a socket while redirect can be replaced by push_patch/2.

For example, mix phx.gen.live Trade Materials materials name:string would generate a LiveComponent in form_component.ex with something like the following in it:

  def handle_event("save", %{"material" => material_params}, socket) do
    save_material(socket, socket.assigns.action, material_params)
  end

  defp save_material(socket, :new, material_params) do
    case Trade.create_material(material_params) do
      {:ok, material} ->
        ...
        {:noreply,
         socket
         |> put_flash(:info, "material created successfully")
         |> push_patch(to: socket.assigns.patch)}
      ...
    end
  end

source: phoenix/form_component.ex at v1.6.15 · phoenixframework/phoenix · GitHub

2 Likes

Thank you a lot @codeanpeace, I just tried mix phx.gen.live and start looking to how all that works, I think I had an idea about that after reading docs from the given links, so I decided to do my own implementation and to go slowly to understand how things are going behind the scenes, my issue now is how to switch between templates in LiveView ?
render/3 used by controllers work just with Regular HTTP since it require conn argument, I searched about and I didn’t find something except this doc which switch templates by creating a global template and then switch depending on the live_action socket’s parameter,

<%= if @live_action == :edit do %>
  <%= render("form.html", user: @user) %>
<% end %>

once push_patch/2 is called, the UserLiveController.handle_params/3 is called with live_action bound to the value specified in the router,
for example :new as below shows :

live "/users", UserLiveController, :index
live "/users/new", UserLiveController, :new
.... 

And then assign some parameters to the socket state to trigger again the global template with a new binding for live_action and the previous parameters to work with them in the new template
But even this approach didn’t work for me because of undefined function render/2 error and when I searched about I found here that this function does not work in templates so I just want to know how to switch “freely” between many LiveView templates as we do with Regular HTTP

As a heads up, I would suggest not naming these LiveView modules with “Controller” as a suffix. It can be confusing for others since it goes against Phoenix conventions which would be either UserLive for LiveViews or UserController for regular “deadview” controllers and not UserLiveController.

There’s not enough shared for me to know for sure, but the approach you quoted should work and should not result in an undefined function render/2 error. I would double check to make sure you’re including the right modules e.g. use HelloWeb, :live_view.

2 Likes

I did all that, creating live/user_live folder, renaming user_live_controller.ex to user_live.ex… in resume I copied all that from a prototype structure created by mix phx.gen.live and it didn’t work and the same error still there.
So I decided to follow the prototype project and switch to live_components, now it works perfectly and I can play around by transition between different live routes, so I think this is the only way to do that in Phoenix 1.7.