Components
Multiple slots
Read time: 3 minutes
Let's recap the solution we used in the previous lesson for the Hero component:
# ...
slot :inner_block, required: true
def hero(assigns) do
~H"""
<div class="bg-gray-800 text-white py-20">
<div class="container mx-auto text-center">
<h1 class="text-4xl font-bold"><%= render_slot(@inner_block) %></h1>
<p class="mt-4 text-lg">My personal website</p>
<.link
class="mt-8 bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded"
navigate={~p"/other"}
>
Get Started
</.link>
</div>
</div>
"""
end
# ...
We use the @inner_block
slot to render the main text of the page but left two other texts repeated both on the home page and on the /other
LiveView. This creates even more confusion because you are already on that page, there is no need for you to see this link.
#Custom slots
By default every component will have an @inner_block
slot if there is any HTML inside its tag. However you can also add more slots as needed. Create and run custom_slots.exs
:
Mix.install([
{:liveview_playground, "~> 0.1.5"}
])
defmodule CustomRouter do
use LiveviewPlaygroundWeb, :router
pipeline :browser do
plug :accepts, ["html"]
end
scope "/" do
pipe_through :browser
live "/", IndexLive, :index
live "/other", OtherPageLive, :index
end
end
defmodule CoreComponents do
use LiveviewPlaygroundWeb, :html
slot :title
slot :subtitle
slot :inner_block
def hero(assigns) do
~H"""
<div class="bg-gray-800 text-white py-20">
<div class="container mx-auto text-center">
<h1 class="text-4xl font-bold"><%= render_slot(@title) %></h1>
<p class="mt-4 text-lg"><%= render_slot(@subtitle) %></p>
<%= render_slot(@inner_block) %>
</div>
</div>
"""
end
end
defmodule IndexLive do
use LiveviewPlaygroundWeb, :live_view
import CoreComponents
def render(assigns) do
~H"""
<.hero>
<:title>IndexLive</:title>
<:subtitle>Welcometo my personal website!</:subtitle>
<.link
class="mt-8 bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded"
navigate={~p"/other"}
>
Get Started
</.link>
</.hero>
<.link navigate={~p"/other"}>Go to other</.link>
"""
end
end
defmodule OtherPageLive do
use LiveviewPlaygroundWeb, :live_view
import CoreComponents
def render(assigns) do
~H"""
<.hero>
<:title>OtherPageLive</:title>
<:subtitle>You're on the first step!</:subtitle>
</.hero>
<.link navigate={~p"/"}>Go to home</.link>
"""
end
end
LiveviewPlayground.start(router: CustomRouter, scripts: ["https://cdn.tailwindcss.com"])
The first modification we made was to add two new slot
to our component definition. To make things clearer we use the names :title
and :subtitle
for the new slots.
Using custom slots is very similar to component syntax except that you must use :
. When we put text in <:my_slot >Abc</:my_slot>
the HEEx code inside it will be sent to this named slot as @my_slot
. Any HTML not within a named slot will be placed in the @inner_block
slot.
The render_slot/2
function can understand when there is nothing in the slot. In the OtherPageLive view we did not add anything outside of named slots and even so there were no problems in the code.
#Recap!
- Components can have more than one slot.
-
Named slots can be used as
<:name>Content</:name>
and rendered as<% render_slot(@name) %>
. -
Any HEEx outside of named slots falls into the
@inner_block
slot.
Feedback
Got any feedback about this page? Let us know!