Componentes
Slots com atributos
Tempo de leitura: 4 minutos
Em nossa aula anterior utilizamos o componente <.hero>
duas vezes. Digamos que na página inicial gostaríamos de que o título fosse mais chamativo. Como passar atributos para slots?
#Entendendo de verdade o que são slots
Para entender o que estamos prestes a fazer você primeiro precisa entender como slots funcionam internamente. Você já deve ter notado que como nós renderizamos slot usando variáveis @nome_do_slot
isso significa que slots não são nada mais que assigns especiais. Todo @nome_do_slot
é obrigatoriamente uma lista. Se você for no seu componente e usar <%= inspect(@nome_do_slot) %>
você verá algo como:
[%{inner_block: #Function<2.32079264/2 in IndexLive.render/1>, __slot__: :nome_do_slot}]
Ou seja, secretamente slots são listas de mapas que contém uma função de renderizar HEEx na propriedade __iner_block__
e o nome do slot na propriedade __slot__
. Dito isso, nada impede de você usar o mesmo slot múltiplas vezes no mesmo componente.
<.hero>
<:title class="text-red-500">IndexLive</:title>
<:title class="text-red-500">IndexLive</:title>
<:subtitle>Welcometo my personal website!</:subtitle>
</.hero>
No exemplo acima, ao inspecionar o slot @title
veremos:
[%{inner_block: #Function<2.32079264/2 in IndexLive.render/1>, __slot__: :title},
%{inner_block: #Function<3.32079264/2 in IndexLive.render/1>, __slot__: :title}]
#Renderizando atributos de slot
Por que fizemos todo esse rodeio de entender que slots são listas de mapas em Elixir se nosso objetivo é renderizar classes de slots? Simples: se slots são listas, podemos fazer loops e, se cada slot é um mapa, podemos pegar propriedades deles!
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 do
attr :class, :string
end
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 :for={title_slot <- @title} class={["text-4xl font-bold", Map.get(title_slot, :class)]}>
<%= render_slot(title_slot) %>
</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 class="text-red-500">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"])
Nosso slot ganhou algo novo na sua definição! Usando um bloco do
podemos declarar quais atributos são pertinentes a aquele slot em específico. Neste caso, apenas class
.
Além disso, mudamos nossa forma de renderizar o slot para usar um loop :for={title_slot <- @title}
de modo com que conseguimos olhar para cada utilização de <:title>
individualmente para pegar suas classes. Dentro do atributo class
usamos uma lista para poder aplicar os atributos opcionais que conseguirmos extrair usando Map.get(title_slot, :class)
(que será nil
por padrão, resultando em nenhuma classe sendo aplicada). Por fim, dentro do nosso loop modificamos a utilização do render_slot/2
para que ele use a variável de loop atual <%= render_slot(title_slot) %>
.
Excelente! Agora seus slots podem ter atributos especificamente neles. Conseguimos resolver o problema original: na página inicial gostaríamos que o slot title tivesse um atributo diferente da outra!
#Resumindo!
- Cada slot é na verdade um assign do tipo lista de mapas.
-
Slots podem receber atributos e podemos documentar isso usando
slot/2
com um blocodo
. -
Para acessar os atributos de slots precisamos fazer um loop no
@nome_do_slot
. Em seguida basta usar umMap.get(item_do_loop, :nome_do_atributo)
.
Feedback
Você tem algum feedback sobre esta página? Conte-nos!