Distribuição de conteúdo com Slots

Em todos os meus artigos tento trazer algumas curiosidades ou soluções de problemas que tenho nas aplicações que desenvolvo diariamente. O problema resolvido a algumas semanas atrás era o seguinte:

Temos várias modais no sistema. Todas podem possuir um cabeçalho, um corpo e um rodapé. Gostaríamos de criar uma modal única que representasse todas as outras modais do sistema. Obviamente, o conteúdo da mesma seria modificado, de acordo com o contexto em que ela estivesse.

Pensamos em várias soluções, porém, um amigo de trabalho disse que tinha visto algo na documentação do Vue.js que solucionaria o problema.

Slots

A grosso modo, Slots são blocos no seu componente onde espera-se que conteúdo seja injetado. Sim, eu sei que isso é simples demais, mas aqui está um exemplo:

<!-- Componente Modal -->
<template>
    <div class="modal">
        <div class="modal-header">
            <slot name="header"></slot>
        </div>
        <div class="modal-body">
            <slot name="body"></slot>
        </div>
        <div class="modal-footer">
            <slot name="footer"></slot>
            <slot>Olá, eu funciono como um "catch"!</slot>
        </div>
    </div>
</template>

Se a pergunta que você está se fazendo agora é “como de fato usar esse slot”, olha só que legal:

<modal>
    <span slot="header">Eu sou um header!</span>
    <span slot="body">Eu sou um body!</span>
    <span slot="footer">Eu sou um footer!</span>
</modal>

O resultado, após a modal ser renderizada, será este:

<div class="modal">
    <div class="modal-header">
        <span slot="header">Eu sou um header!</span>
    </div>
    <div class="modal-body">
        <span slot="body">Eu sou um body!</span>
    </div>
    <div class="modal-footer">
        <span slot="footer">Eu sou um footer!</span>
    </div>
</div>

Perceba que no componente Modal eu tenho quatro slots: três nomeados e um anônimo. Perceba também que na renderização final o slot anônimo não apareceu. Como ele próprio já denota, ele é um slot tipo “catch”, ou seja, se houvesse qualquer slot não nomeado ou com um nome não presente na modal, o conteúdo deste seria substituído no catch. Ou seja:

<modal>
    <span slot="header">Eu sou um header!</span>
    <span slot="body">Eu sou um body!</span>
</modal>

removendo o slot “footer”, a renderização final do componente Modal seria:

<div class="modal">
    <div class="modal-header">
        <span slot="header">Eu sou um header!</span>
    </div>
    <div class="modal-body">
        <span slot="body">Eu sou um body!</span>
    </div>
    <div class="modal-footer">
        Olá, eu funciono como um "catch"!
    </div>
</div>

Abaixo temos um exemplo funcional de slots. Caso o conteúdo não apareça, clique neste link.