{"id":7515,"date":"2018-05-16T17:53:04","date_gmt":"2018-05-16T20:53:04","guid":{"rendered":"http:\/\/blog.plataformatec.com.br\/?p=7515"},"modified":"2018-05-17T16:50:36","modified_gmt":"2018-05-17T19:50:36","slug":"nested-layouts-with-phoenix","status":"publish","type":"post","link":"http:\/\/blog.plataformatec.com.br\/2018\/05\/nested-layouts-with-phoenix\/","title":{"rendered":"Nested Layouts With Phoenix"},"content":{"rendered":"
Over the last few weeks, we have been building a web application in one of our clients and ended up duplicating some template code. These new pages had something in common between them, but not with the rest of the application. We needed an inner layout to reuse the template code between these pages, however, Phoenix doesn’t come with this feature. In this post, you’ll learn how you can build nested layouts in Phoenix and when you should use them.<\/p>\n
Inner (or nested) layouts are useful to reuse template code when you have subsections on your website. Usually, in Phoenix applications, we have <\/p>\n <\/p>\n In the example above, we want to reuse the application logo, title, and navigation menu across all pages. We can solve that by using the Phoenix default layout and it works great. However, sometimes you need to create a new website section inside the parent layout. For example, imagine we want to add a help section to our Phoenix website. Look at the image below:<\/p>\n <\/p>\n We still want to reuse the layout header across all pages, but we also want to reuse the left navigation and main content layout in all pages inside the help section. Let’s see how we can do that using Phoenix.<\/p>\n I tried some solutions by looking at some examples online. After discussing with the Plataformatec team, we came up with an approach that’s very simple and extensible, thanks to Phoenix explicit layout rendering. The solution works like this:<\/p>\n <\/p>\n The main goal here is to make the nested layout work like any Phoenix layout. This will make it familiar to any Phoenix developer.<\/p>\n First, let’s add a function that allows a template to render the parent layout:<\/p>\n The With the code above, our parent layout is aware that there may be an inner layout and it should render its contents when available. That’s it! Now let’s see how we can use it.<\/p>\n You can create a nested layout template in the We render the parent and put the inner content in the Now you can use our nested layout by invoking the plug You can organize your nested layout files in a different way. For example, imagine you have a help section in your website and you want to keep the nested layout file in the After that, you can put your nested layout template in The code above will render your Inner layouts come in handy to reuse template code of subsections of your web application. You learned how simple it is to build that in Phoenix. Just be aware of not creating inner layouts of inner layouts. If you do that, your codebase will start to be very hard to maintain. You can see and try by yourself a sample Phoenix app<\/a>\u00a0running the inner layout example.<\/p>\n Have you implemented inner layout in a different way? Do you have any feature that you would like to see how we can build it in Phoenix? Let us know in your comments.<\/p>\n","protected":false},"excerpt":{"rendered":" Over the last few weeks, we have been building a web application in one of our clients and ended up duplicating some template code. These new pages had something in common between them, but not with the rest of the application. We needed an inner layout to reuse the template code between these pages, however, … \u00bb<\/a><\/p>\n","protected":false},"author":31,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[1],"tags":[143,245],"aioseo_notices":[],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/7515"}],"collection":[{"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/users\/31"}],"replies":[{"embeddable":true,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/comments?post=7515"}],"version-history":[{"count":22,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/7515\/revisions"}],"predecessor-version":[{"id":7540,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/7515\/revisions\/7540"}],"wp:attachment":[{"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/media?parent=7515"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/categories?post=7515"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/tags?post=7515"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}\/templates\/layout\/app.html.eex<\/code> that shares template code between all pages. For example:<\/p>\n
The Inner Layout Solution<\/h2>\n
\n
\u00a0Preparing The Parent Template<\/h2>\n
# views\/layout_view.ex\ndefmodule YourAppWeb.LayoutView do\n use YourAppWeb, :view\n\n def render_layout(layout, assigns, do: content) do\n render(layout, Map.put(assigns, :inner_layout, content))\n end\nend\n<\/code><\/pre>\n
render_layout\/3<\/code> will render the given layout by assigning the contents of the inner layout given in the
do<\/code> argument. Now, in the parent layout, we need to render the inner layout contents or the controller view contents. Look at how you can do it:<\/p>\n
<%# templates\/layout\/app.html.eex %>\n\n<%# ... %>\n\n <%= Map.get(assigns, :inner_layout) || render @view_module, @view_template, assigns %>\n\n<%# ... %>\n<\/code><\/pre>\n
Using the Nested Layout<\/h2>\n
layouts<\/code> folder and use it like this:<\/p>\n
<%# templates\/layout\/nested_layout.html.eex %>\n\n<%= render_layout \"app.html\", assigns do %>\n <%# Your HTML markup here %>\n <%= render @view_module, @view_template, assigns %>\n <%# More HTML markup here %>\n<% end %>\n<\/code><\/pre>\n
do\/end<\/code> blocks in our nested layout template. Any content outside of the
do\/end<\/code> blocks will not be rendered. Don’t forget to call
render @view_module, @view_template, assigns<\/code>, or the contents of your controller’s action template will not be rendered.<\/p>\n
put_layout\/2<\/code> in your controller or router. For example:<\/p>\n
defmodule YourAppWeb.NestedContentController do\n use YourAppWeb, :controller\n\n plug :put_layout, :nested_layout\n\n def index(conn, params) do\n # stuff\n end\nend\n<\/code><\/pre>\n
help<\/code> folder. In your
HelpView<\/code> you’ll need to import the
render_layout\/3<\/code> function, like this:<\/p>\n
# views\/help_view.ex\n\ndefmodule YourAppWeb.HelpView do\n use YourAppWeb, :view\n import YourAppWeb.LayoutView, only: [render_layout: 3]\nend\n<\/code><\/pre>\n
templates\/help\/layout.html.eex<\/code>. In the controller or router, you can invoke the nested layout like this:<\/p>\n
plug :put_layout, {YourAppWeb.HelpView, \"layout.html\"}\n<\/code><\/pre>\n
layout.html.eex<\/code> template in
views\/help<\/code> directory using
YourAppWeb.HelpView<\/code> module.<\/p>\n
Wrapping Up<\/h2>\n