Embedding live view in eex :not_mounted_at_router

I am trying to embed a live view based form in a normal web page so I can get the nice validation etc. I am hitting the warning on :not_mounted_at_router, I see comments on github threads that I should declare LiveView in the router, but I am not quite sure what this actually means. I have the live view paths in the root scope but clearly this isn’t it.

1 Like

What triggers the warning in your case?

There are a few limitations for liveviews not mounted at the router level: no access to params in mount, you can’t do live navigation unless you pass the :router option but, even so, still limited to one liveview with router per page .etc

1 Like

Thanks, I didn’t know that. I have table of 4 x 4 and I wanted each one to be a live view which a user could click and edit the data of. I suppose to achieve this I should create 1 live view which renders the entire collection?

That would work, yes. You could also extract the whole editing part into a separate component.

If I may suggest, as an example, you could generate a new phoenix project passing in the --live flag, then generate a simple context and it will be setup exactly like this (check video of this blog post on the Phoenix’s website…first 3 minutes)

How does the :router option work here, please? I cannot find any documentation here.

I found that I can pass param down to child liveview via :session, but cannot find out how the router option should work…

1 Like

Here are the docs. This is for live views rendered either directly from a controller or as part of a phoenix view.

Yes, I’ve looked at exactly that doc page. It says:

  • :router - an optional router that enables this LiveView to perform live navigation. Only a single LiveView in a page may have the :router set. LiveViews defined at the router with the live macro automatically have the :router option set.

But I don’t understand how it is used. What is the syntax there? What do I actually need to pass there?

I have a standard phoenix view and inside it I include a live view like this:

<%= live_render(@conn, EnrolWeb.EnrollmentLive.Index, session: %{
            "class_id" => @class.id
          })  %>

But I am running into this error

ArgumentError at GET /classes/15
cannot invoke handle_params/3 on EnrolWeb.EnrollmentLive.Index because it is not mounted nor accessed through the router live/3 macro

and I don’t really understand how to properly “mount” the router so that this works.

Thank you

1 Like

Pass the router module, like this:

<%= live_render(@conn, EnrolWeb.EnrollmentLive.Index, 
          session: %{
            "class_id" => @class.id
          router: EnrolWeb.Router)  %>

I think that’s all there is to it.


thank you, that works! why I always keep thinking of complex solutions without trying the simple and obvious one first :see_no_evil:

One more thing I don’t understand here, please.

I pass the router as suggested above router: EnrolWeb.Router. The result of doing this is that my liveview mount function receives params, that in my case include the "class_id" so I wouldn’t need to pass it explicitly in session.

However, the params are included only on the first pass of mount function, i.e. the initial “non-live” render. But once the socket connection is established and mount is called the second time, it receives :not_mounted_at_router in place of params. This in results crash due to not having any “id” key in my params…

Here is my simplified version using the params that would crash on second pass:

def mount(%{"id" => class_id} = _params, _session, socket) do
    {:ok, assign(socket, :class_id, class.id)}

Here is what I do instead:

def mount(_params, session, socket) do
    class_id = session["class_id"]
    {:ok, assign(socket, :class_id, class_id)}

But that way I am ignoring the params altogether and the :router option in live_render has no purpose, does it?

So the question is whether the :not_mounted_at_router is expected on second pass? If yes, what is the point of passing the router, or how can it be utilized? I can potentially use if connected?(socket) to distinguish between first and second pass, but is that the correct way to go about it? And are there any significant drawbacks in using just session and thus ignoring this whole problem? I don’t want to ignore it, I want to understand it first :slight_smile:

Thank you

1 Like

Yeah, I think the router option is not going to work. You will have to mount it at the router (to use live navigation) or it has no effect. I will update the docs to no longer mention it. Thanks!