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, ',', '.')],
];
}
}
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 HTML | O client faz um novo GET (round-trip) | O servidor atual monta e renderiza, sem round-trip |
| Quando termina | Depois, assíncrono | Já, dentro da resposta atual |
| Quem pode disparar | Qualquer 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]);
}
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 escalar | Mudar 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 listagem | manageRow($id, $gridClass) |
| Atualizar um card de kanban | manageCard($id, $kanbanClass) |
| Substituir um trecho de HTML qualquer | html($seletor, $html) |
| Recarregar um relatório/resumo via método estático | reload($seletor, 'Classe@metodo', $params) |
| Recarregar um MadComponent inteiro, por nome, de outro lugar da página | energize($nome, $params) + <mad-transporter name="..."> |
| Injetar um MadComponent inteiro, já pronto, na resposta atual | teleport($seletor, $classe, $metodo, $params) |
| Abrir um componente sem se importar com o wrapper dele | MadResponse::open($classe, $params) |