Phoenix Multiple Layouts

Hello, Im having issue, having multiple layouts.

  1. Layouts for Admin (System Access / Portal / Data Management)
  2. Layouts for Login / Register

I create new plug for router just to handle different layouts for login

pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, html: {WebApp.Layouts, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
    plug :fetch_current_user
  end

  pipeline :login_page do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, html: {WebApp.AuthenticationLayouts, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
    plug :fetch_current_user
  end

And I copy the original layouts and make the layouts components as below :

image

This will make whenever I visit link /users/log_in (Using Original Phx Auth) it will render root file from folder components auth_layouts.

However when I use code <%= @inner_content %> inside root file auth_layouts/root.html.heex it will render page from file layouts/app.html.heex instead of auth_layouts/app.html.heex. Please suggest/assist where can I change to make it render auth_layouts/app.html.heex. Thanks. Any response is highly appreciated

side note : Sorry if the question kinda noob, this is my first project using Phx Framework. Excited but can’t find the solution.

1 Like

I’m not the best person to answer this, but one thing that immediately comes to mind is have you checked your web_app.ex file (making assumptions about your naming based on what I see in your code example) and what it may/may not do with layouts? By default there’s some layout assignment happening there and my recollection was that it could interfere with the plug based stuff.

I use different layouts as well and rather than trying to control layouts via plugs, I created entries in my equivalent to web_app.ex to deal with the layouts and other kinds of setup as appropriate to the type of form.

Again… I could be way off-base and there are others here that can probably be more helpful…

2 Likes

Hello,

Yes I already tried that solution, however this only applicable for 1 layouts, I’m not sure how to register multiple layouts. Here what I do

def live_view do
    quote do
      use Phoenix.LiveView, layout: {WebApp.Layouts, :app}

      unquote(html_helpers())
    end
  end

Change to

def live_view do
    quote do
      use Phoenix.LiveView, layout: {WebApp.AuthenticationLayouts, :app}

      unquote(html_helpers())
    end
  end

This will successfully render auth_layouts/app.html.heex. However what If I wanna use the original layouts?

Ya, this is what I do.

I don’t have multiple directories for layouts since there is only one layout per different section but I have;

my_app_web/layouts/root.html.heex
my_app_web/layouts/public.html.heex
my_app_web/layouts/admin.html.heex
def live_view do
  quote do
    use Phoenix.LiveView,
      layout: {GelaWeb.Layouts, :public}

    unquote(html_helpers())
  end
end

def admin_live_view do
  quote do
    use Phoenix.LiveView,
      layout: {GelaWeb.Layouts, :admin}

    unquote(html_helpers())
  end
end

Then:

defmodule MyAppWeb.SomeAdminPage do
  use MyAppWeb, :admin_live_view
end

You can do the same with the controller helper.

I’m sure you can make this work with directories if you use a string instead of an atom.

3 Likes

[I had posted my code, but @sodapopcan did so before me and mine had nothing new to offer on that response… can’t delete my comment so… clearing it.]

1 Like

Ha, sorry, didn’t realize you were replying!!

1 Like

You can return a layout from mount/3 like so: {:ok, socket, layout: {module, atom}}. You can also use live_session to change the layout for a bunch of liveviews. No need to drop down to the macros to do that.

5 Likes

Thanks @sodapopcan This give me the idea. I can create a custom function, and on a specific / any live view that I need to be different layouts, I can use that custom function. Thanks.

Below are my solution. Create new custom function on web_app.ex

def live_view_login do
    quote do
      use Phoenix.LiveView, layout: {WebApp.AuthenticationLayouts, :app}

      unquote(html_helpers())
    end
  end

and on LiveView use use WebApp, :live_view_login instead of using use WebApp, :live_view_login (the original)

Thanks guys @sodapopcan @sbuttgereit @LostKobrakai , Really appreciate the help. Resolved my issue.

2 Likes

Ya, I remember I was doing with this then switched to the macro helper version, I can’t remember quite why. I do add some extra helpers in the admin route though those could probably be moved to hooks ← nm, that doesn’t make any sense, lol. Ya, I think it was just because I had additional helpers but that was so long ago now that it is something I just do. I could re-evaluate.

I’d give heed to @LostKobrakai’s answer, though: you can also pass a layout to live_session.

Thanks, This also will allow to override the layouts

In my experience this does not work. It returns something like:

** (ArgumentError) :layout expects a tuple of the form {MyLayoutView, :my_template} or false, got: “pos.html”
(phoenix_live_view 0.19.5) lib/phoenix_live_view/utils.ex:216: Phoenix.LiveView.Utils.normalize_layout/2
(phoenix_live_view 0.19.5) lib/phoenix_live_view/router.ex:276: anonymous fn/3 in Phoenix.LiveView.Router.validate_live_session_opts/3
(elixir 1.15.2) lib/enum.ex:2510: Enum.“-reduce/3-lists^foldl/2-0-”/3
(phoenix_live_view 0.19.5) lib/phoenix_live_view/router.ex:240: Phoenix.LiveView.Router.live_session/3
lib/selfpos_web/router.ex:82: (module)

The new format requires – as stated – {module, atom} format not {module, string} or just string.

thank you, I am struggling to find where in the docs it defines what should be in the tuple. Sticking to the overrides for now. Maybe if I continue learning the platform I will figure it out someday.