Can't use put_layout with liveviews on router?

Hello !

I’ve been trying to figure out the best way to assign a layout to my liveviews loaded from the router. I have (at the moment) two distinct layout (in addition to the root, set with the plug put_root_layout) that i need to assign to two different list of liveviews.

I’ve tried to use put_layout in a specific pipeline that i then scope like this:

pipeline :live do
  plug :put_layout, {MyAppWeb.LayoutView, :live}
end

pipeline :live_settings do
  plug :put_layout, {MyAppWeb.LayoutView, :live_settings}
end

scope "/", MyAppWeb do
  pipe_through [:main_pipeline, :app]

  live "/", FirstLive
end

scope "/settings", MyAppWeb do
  pipe_through [:main_pipeline, :settings]

   live "/settings", SecondLive
end

I am aware of both options to either pass a layout option to the live macro, or declaring it like the phoenix generator does inside a quote with use Phoenix.LiveView, layout: {MyAppWeb.LayoutView, "live.html"} (note that i removed that one to instead do it in the router). I just find it clearer when used in the router through a pipeline.

I’ve found a few confusing things about this on the forum (like the now removed put_live_layout), so i’m not sure if doing the way i want just won’t work, or if i must have something on my side preventing it from working properly ?

Note: I’m on liveview 0.13.3. Haven’t seen any fix in the changelog on the newer versions, so i don’t think it’s a bug, unless i missed it.

In the code you shared, you are not actually using the pipelines :live and :live_settings, but :app and :settings.

Also, the :live layout will be picked up automatically as it’s the default. You don’t need to declare the :live pipeline.

pipeline :live do
  plug :put_layout, {MyAppWeb.LayoutView, :live}
end

Thanks for your reply.

That’s actually a typo, i’m calling the proper pipelines on the original code. Here’s the fixed version (can’t update the original post anymore), without the :live pipeline.


pipeline :live_settings do
  plug :put_layout, {MyAppWeb.LayoutView, :live_settings}
end

scope "/", MyAppWeb do
  pipe_through :main_pipeline

  live "/", FirstLive
end

scope "/settings", MyAppWeb do
  pipe_through [:main_pipeline, :live_settings]

   live "/settings", SecondLive
end

That doesn’t change anything though. It’s still not working, the layout for the “/settings” scope isn’t being picked up. That’s a proper way to do it though, right ? Like it’s an issue on my part, and not something i shouldn’t try ?

Can you share :live_settings template? Are you calling <%= @inner_content %> inside it?

Also, to confirm, in the :main_pipeline you have the put_root_layout plug call?

Both template (:live and :live_settings) call <%= @inner_content %>. If i set either one of them directly with use Phoenix.LiveView, layout: {MyAppWeb.LayoutView, "live_settings.html"} (or the “live.html” version), it works flawlessly.

Yes, the main pipeline uses put_root_layout (and that one gets picked up properly).

I went to see if put_layout was actually setting the layout properly, and it seems to be the case (the :phoenix_layout property is set to the given layout depending on the scope properly). Despite this, i end up with the root layout being loaded, but not the other one.

After searching in LiveView’s code, i just found this, which i think is related to my problem, with what the live macro in the router is doing: Github LiveView relevant bit.

It looks like put_layout is called again, removing any previous assignment. Can anyone explain the reasoning behind this ?

Have you figured out what’s happening here and why is this the case?

We have changed layouts some point along the way so that:

  1. root_layout is the layout shared between your LiveView and regular code
  2. LiveView have their own layout which is specified inside the liveview (which is also diff/content tracked)

Therefore, we always disable the regular layout because otherwise you would end-up with three. Depending on what you want to achieve, I would rather change the root layout OR move this logic to inside the LiveView layout. I can’t see why you would need a third layout to wrap things around. :slight_smile:

1 Like

I’m not sure, but it seems like I have a very similar problem that I can’t resolve? I want to start phasing Material Design Bootstrap into our site, so I need to start changing parts of the site over (and leave all of the other areas styled as they are). My root.html.leex file in template/mdb_admin is being applied the same as my root.html.leex file in the template/admin folder as expected, but my live.html.leex in template/mdb_admin is not being applied.

So, my question is: Given the pipeline below, should I expect that my live.html.leex in mdb_admin should render instead of the one in layout? Also, is this an advisable way to separate the new portions of the site?

pipeline :mdbadmin do
plug :put_root_layout, {MyCompany.MdbAdminView, :root}
plug :put_layout, {MyCompany.MdbAdminView, :live}
end

The live layout should be configured when you do use Phoenix.LiveView, layout: {MyCompany.MdbAdminView, :live}, not in the router.

2 Likes

Okay, I’ve seen some blog posts where they set up something like below.

Create a custom live layout in myapp_web.ex:

def custom_live_view do

Then, use that custom_live_view in LiveView:
defmodule MyApp.UserLive.Index do
use MyApp, :custom_live_view

Worked for me, thanks!