Docs›Reatividade›Render parcial
Reatividade

Render parcial

manageRow, energize, html() targetado.

Render parcial é o espectro de técnicas que evitam re-renderizar o MadComponent inteiro a cada ação — da menor granularidade (um <input>) até substituir só um pedaço específico do DOM. Quanto mais parcial, mais rápido e menos "flash" visual.

1. val / bind (auto-bind)     menor custo — 1 campo ou 1 span, sem HTML novo
2. html() / manageRow/Card    custo médio  — um trecho renderizado server-side
3. energize() / teleport()    custo médio  — um MadComponent inteiro, parcial
4. full re-render             maior custo  — todo o componente atual de novo

Degrau 1 — val / bind automáticos

O caminho mais barato é não precisar pedir nada: mude uma prop pública escalar e o auto-bind do diff (ver Auto-bind) gera val/bind sozinho. Sem HTML novo trafegando, sem MadResponse explícito.

A op bind vai além do @madBind: ela também patcha o state Alpine de qualquer [x-data] do wrapper que tenha a prop (os scopes de @madWire(['prop'])). Com isso, uma única prop escalar pode dirigir texto, visibilidade e classes em vários pontos da tela — via mad-text, mad-if, mad-show, mad-bind:class — tudo no mesmo request, sem HTML no payload. Ver Diretivas mad-*.

Degrau 2 — manageRow / manageCard / html()

Quando o que mudou é uma linha de uma listagem ou um card de um kanban, renderizar a tela inteira de novo é desperdício — manageRow() e manageCard() buscam o HTML server-side de UM registro e fazem o patch + highlight no DOM.

manageRow — patch numa linha do MadDataGrid

class ProdutoForm extends MadComponent
{
    protected static string $wrapper = self::DRAWER;

    public ?int $registroId = null;
    public MadForm $form;

    public function onSave(): MadResponse
    {
        $this->form->validate(Produto::rules());

        $produto = Produto::findOrNew($this->registroId);
        $this->form->save($produto);

        // Fecha o drawer, mostra toast, e patcha SÓ a row deste produto na
        // listagem por trás — sem recarregar a tabela inteira.
        return (new MadResponse())
            ->toast('Produto salvo!', 'success')
            ->closeDrawer()
            ->manageRow($produto->id, ProdutoListagem::class);
    }

    protected function view(): string|array
    {
        return 'business.produto-form';
    }
}
class ProdutoListagem extends MadDataGrid
{
    // query() retorna ['items' => [...], 'total' => int] — NÃO um Builder.
    protected function query(): array
    {
        $q = Produto::query()->orderBy('nome');
        return ['items' => $q->get()->all(), 'total' => $q->count()];
    }

    protected function columns(): array
    {
        return [
            'nome'  => ['label' => 'Nome'],
            'preco' => ['label' => 'Preço', 'transform' => fn ($v) => 'R$ ' . number_format($v, 2, ',', '.')],
        ];
    }
}
Como funciona por dentro

manageRow() chama MadDataGrid::renderSingleRow($gridClass, $id) — que roda query()/columns() da grid alvo filtrando só por aquele ID e devolve a <tr> (e o card mobile, se houver) já formatada. O client substitui a row existente (por data-row-id) ou insere no topo do <tbody> se for um registro novo.

manageCard / removeCard — patch num card do MadKanban

public function onMoverParaConcluido(int $tarefaId): MadResponse
{
    $tarefa = Tarefa::findOrFail($tarefaId);
    $tarefa->status = 'concluido';
    $tarefa->save();

    return (new MadResponse())
        ->toast('Movido para Concluído!', 'success')
        ->manageCard($tarefa->id, QuadroTarefas::class);
}

public function onExcluirTarefa(int $tarefaId): MadResponse
{
    Tarefa::findOrFail($tarefaId)->delete();
    return (new MadResponse())->removeCard($tarefaId);
}

html() — substituir innerHTML de qualquer seletor

Mais genérico que manageRow/manageCard: qualquer HTML server-side (uma view(), um componente Blade renderizado manualmente) pode substituir o innerHTML de qualquer seletor CSS dentro do wrapper. O caso clássico é a combo cascateada:

<mad-form-grid :cols="2">
    MAD__BLADE_COMMENT__1__
    <mad-dbselect-field name="estado" model="Estado" display="nome"
        mad:change="onChangeEstado" />

    MAD__BLADE_COMMENT__2__
    <div data-mad-target="cidade-combo">
        <mad-dbselect-field name="cidade" :items="[]" disabled
            placeholder="Selecione um estado antes" />
    </div>
</mad-form-grid>
public function onChangeEstado(string $estado): MadResponse
{
    $this->cidade = '';

    $html = MadBlade::render('components.dbcombo-field', [
        'name'        => 'cidade',
        'label'       => 'Cidade',
        'display'     => 'nome',
        'orderBy'     => 'nome',
        'query'       => Cidade::where('estado_id', $estado)->orderBy('nome'),
        'disabled'    => false,
        'placeholder' => 'Selecione...',
    ]);

    return (new MadResponse())->html('[data-mad-target="cidade-combo"]', $html);
}

reload() — chamar um método estático e injetar o retorno

Quando o trecho a recarregar não é um campo nem uma row/card padrão — é um relatório, um resumo, uma tabela auxiliar — reload() chama um método static que faz echo do HTML:

// reload() chama um metodo ESTATICO — o que ele faz "echo" vira o innerHTML do alvo.
class PedidoForm extends MadComponent
{
    public static function renderItens(int $pedido_id): void
    {
        $itens = PedidoItem::where('pedido_id', $pedido_id)->get();
        echo view('pedido.partials.itens', ['itens' => $itens])->render();
    }

    public function onItemAlterado(): MadResponse
    {
        return (new MadResponse())
            ->reload('#painel-itens', 'PedidoForm@renderItens', ['pedido_id' => $this->pedidoId]);
    }
}

Degrau 3 — energize() e teleport()

Quando a unidade de render parcial é um MadComponent inteiro (não um trecho de view), existem dois caminhos — a diferença é quem dispara o segundo request.

energize()teleport()
Quem busca o HTMLO client faz um novo GET (round-trip)O servidor atual monta e renderiza, sem round-trip
Quando terminaDepois, assíncronoJá, dentro da resposta atual
Quem pode dispararQualquer componente da página, por nome (<mad-transporter name="x"> em outro lugar do DOM)Só o componente que está respondendo agora, mirando um seletor que ele conhece
Uso típico"Atualize o painel financeiro lá embaixo depois que eu salvar aqui em cima""Mostre o detalhe completo deste registro agora, no painel ao lado"

energize()

MAD__BLADE_COMMENT__3__
<mad-transporter name="painel-financeiro" class="PainelFinanceiro" method="show" />
// Qualquer action em QUALQUER componente da página pode pedir o reload —
// energize() dispara window.dispatchEvent('mad:energize', {name, params}),
// e o transporter que tiver esse mesmo "name" refaz o GET sozinho.
public function onFecharPeriodo(): MadResponse
{
    // ...
    return (new MadResponse())
        ->toast('Período fechado!', 'success')
        ->energize('painel-financeiro', ['periodo' => $this->periodoAtual]);
}

teleport()

// teleport() NÃO faz round-trip — monta a classe, chama mount()+method() e
// injeta o HTML já pronto na MESMA resposta. Útil quando você já sabe, no
// servidor, exatamente o que precisa aparecer (sem esperar um 2º request).
public function onVerDetalhe(int $id): MadResponse
{
    return (new MadResponse())
        ->teleport('#painel-detalhe', DocumentDetail::class, 'onShow', ['id' => $id]);
}
MadResponse::open() para o caso geral

Se o objetivo é simplesmente "abrir esse componente" e ele já sabe se é DRAWER/MODAL (overlay) ou INTERNAL (navegação), normalmente nem energize nem teleport são necessários — MadResponse::open('Classe', $params) resolve isso sozinho. Ver MadResponse — todas as ops.

Degrau 4 — quando o full re-render é inevitável (e correto)

Full re-render é o fallback padrão e seguro do auto-bind: objetos em props públicas e arrays sem registro no MadVarRegistry sempre disparam re-render completo do componente — não é um bug, é a garantia de que o HTML nunca fica dessincronizado do state real.

// Dispara FULL re-render do componente (fallback seguro e padrão):
public array  $itens = [];          // array sem registro de autocomplete
public ?Model $registro = null;     // qualquer objeto na prop pública
public array  $itensAvulsos = [];   // field-list cujo tipo de coluna não tem reload_* mapeado

// Permanece PARCIAL (val/bind/reload_*):
public string $nome = '';           // escalar registrado em campo de formulário
public int    $contador = 0;        // escalar — sempre gera ao menos "bind"
public array  $metodos = [];        // array registrado via data-mad-autocomplete="metodos"

Detalhes de quando cada caminho é escolhido automaticamente: ver Auto-bind.

Resumo de decisão

Preciso...Usar
Atualizar um campo/valor escalarMudar a prop — auto-bind cuida (val/bind)
Refletir uma prop escalar em vários pontos da tela (texto, visibilidade, classe)@madWire(['prop']) + mad-text/mad-if/mad-show — a op bind patcha o Alpine sozinha
Atualizar uma row de listagemmanageRow($id, $gridClass)
Atualizar um card de kanbanmanageCard($id, $kanbanClass)
Substituir um trecho de HTML qualquerhtml($seletor, $html)
Recarregar um relatório/resumo via método estáticoreload($seletor, 'Classe@metodo', $params)
Recarregar um MadComponent inteiro, por nome, de outro lugar da páginaenergize($nome, $params) + <mad-transporter name="...">
Injetar um MadComponent inteiro, já pronto, na resposta atualteleport($seletor, $classe, $metodo, $params)
Abrir um componente sem se importar com o wrapper deleMadResponse::open($classe, $params)

Próximos passos