-
Notifications
You must be signed in to change notification settings - Fork 939
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature Request / Discussion: Slots in Layouts #2586
Comments
I think that ends up being too magical IMO. What I typically do is that I wrap my templates in an additional component if necessary and keep the layout simpler. |
What I do not like with that solution is the need to pass all the assigns to the layout component, e.g. if the layout uses the def render(assigns) do
~H"""
<.my_layout page_title={@page_title} user={@current_user} ...>
<:my_slot>...</.my_slot>
Other content
</.my_layout>
"""
end as using |
Hi @SteffenDE! This is a major drawback for one particular use case I'm having as well. You can read more about this on the forum: https://elixirforum.com/t/how-are-you-doing-nested-layouts-with-phoenix-1-7/55236/11. But the gist of the problem is what you already mentioned; the fact that layouts are not actually behaving as standard functional components. @josevalim I think this definitely should be looked into. If layouts are meant to be called just like function components, |
We don't want layouts to become full-blow function components because layouts are rendered before LiveView. For example, the root layouts comes into play before anything else is rendered, regardless if you have LiveView or not. The solution to your problems, both here and the forum, is to keep the layout minimal and call shared function components in your templates/layouts. AFAIK, this is also how it is done in the JS community and also in Django. So, at the moment, we don't plan to add more magic to layouts. |
Hi @josevalim! Thanks for the explanation...
I just one to ask for a favor then... Could you please give an example of how to achieve this? If possible even, reply on the forums so we have a documented solution. I'm asking because this sentence sounds rather abstract to me (I thought I was already doing what you are saying). Since I can basically just call a layout like a function component, it seems a little bit counter-intuitive that what gets passed to it it's not a Another problem is that I was under the impression that this explanation was true:
So, imagine my surprise that while calling a layout as a component, I get an error that says that
I might be misreading this, but it sounds like (when you first read it) we should expect some parity for all HTML rendering inside the framework because it is all based on function components. PS.: Sorry if this sounds like a stupid question, but if not's immediately obvious to me what should be done here 😅. |
Instead of trying to pass slots to a layout, have shared function components that you build your layouts. Let's imagine you are building a school website. You have "students" layout and a "teachers" layout. They are similar layout with different links and buttons. Don't try to create a "generic" layout and then extend it in both "students" and "teachers". Instead, create a set of generic components and slots, such as
Your starting point is the function component not the layout. That's what I mean by keeping the layouts minimal. If you want to share something, use function components to share it, not the layouts themselves.
Yes, they are all function components, but you don't expect all function components to receive the exact same arguments. Each receive their own and you need to adept accordingly. Like any other function. |
Btw, what I mentioned above is the same technique The general structure is defined as a bunch of function components, such as |
Hi @josevalim! Sorry, but I found your example a little confusing and I'm kind of on a dead end here... Could you help me see how that example I gave would work? The use case is fairly simple and we were already able to do that previously. As a side note, I'm not understanding why we can't rename the assigns from layouts from admin layout: <main>
<.flash_group flash={@flash} />
<!-- Something like this would even work -->
<!-- <%= assigns[:inner_content] || render_slot(@inner_block) %> -->
<%= render_slot(@inner_block) %>
</main> Settings layout (and this already works): slot :inner_block, required: true
def settings(assigns) do
~H"""
<.admin flash={@flash}>
<header>Settings</header>
<%= render_slot(@inner_block) %>
</.admin>
"""
end What I want to achieve is that a random edit page can be rendered inside the settings layout that is rendered inside the admin layout. It all works except for the fact that the assigns for the "contents" are different when the layout is called from other places and when it's called from another function component: PS.: Man, I'm really sorry but this is not intuitive to me (at all) 😣 Update: I just want to make a correction from my previous statement... It seems that when I'm rendering an edit page from the controller passing this: |
I am bowing out of this discussion. I have said above that you should not try to extend layouts. Don't make the I recommend continuing the discussion in the forum. |
No problem, that's cool... I think this issue should at least be left open and considered more carefully because the current experience is at least confusing if you are used to how things work with Phoenix pre-1.7 (I know I'm having to teach this to at least three other people besides myself). The documentation states that "Layouts are just function components" but they are not quite the same - to be fair, the docs say that contents for the layouts will be placed inside an And now, things look the same but behave differently - so either layouts are in fact, a "special type of function component" or they are "just function components". I'm sure there might be some technical concerns like you said that I'm unaware of, specifically because the whole implementation is very recent; but I hope the user experience is taken into consideration for the future. Cheers! |
I have already addressed this. It is not because something is a function component that it means they accept slots. For example, Since we are clearly going in circles, I will lock the thread to avoid more circling around. Especially because this is not an bug and I believe the forum will provide better support, as it is clear that my attempts at explaining here have not been enough, and others have already provided similar answers in the forum. |
Hello there,
I want to discuss a thought that I recently had about the use of function components in layouts. As the Phoenix documentation states (https://hexdocs.pm/phoenix/components.html#layouts):
Currently, there are some inconsistencies and one major feature missing (in my opinion) that I think could really have some nice benefits.
Imagine that you have a layout defined that includes a heading section with some content that is conditionally rendered on the right side, like the buttons in this image:
These buttons could be defined in the layout (e.g. app.html.heex) like this:
In the LiveView we then define them as an assign:
Now, we want to modify the section right to the title to allow arbitrary content, so we adapt the code to use something like this, basically writing our own very crude slot implementation:
And in our LiveView we could do the follwing:
I've come across this use case of having something like "slots" in the layout more than once now and as layouts are mostly function components now, I imagined being able to do something like this instead:
This is currently not a valid layout component, because layouts use
@inner_content
instead of the@inner_block
slot, but defining slots is actually working, just without any way to use them. Wouldn't it make sense to move to theinner_block
slot for layouts too?Currently, code like this leads to the following error:
Of course there always is the option of moving code from the layout into a component and then using this component in every LiveView, e.g.
but I really think that if every page ends up including something like this it's better placed in the layout instead.
So I wanted to ask here: what do you think about this? Are there any major drawbacks that I am missing? Do you have any other nice solutions for "slots in layouts"?
The text was updated successfully, but these errors were encountered: