set() e setItems()
Auto-bind: mude estado, framework gera ops.
MadForm::set() e MadForm::setItems() mudam o estado de
campos do formulário via PHP. O framework detecta o diff do estado após a action e
gera as ops parciais adequadas para atualizar o DOM —
sem full re-render e sem MadResponse explícito.
Mude o estado no PHP, deixe o framework cuidar do DOM.
Quando usar cada um
Form principal
| Preciso atualizar... | Método | Op gerada |
|---|---|---|
| Valor de um campo (input, textarea, select, date, etc.) | form->set($name, $value) | val |
Options de um combo/select (mad-select-field / mad-dbcombo-field) | form->setItems($name, $items) | reload_combo |
Options de um radio (mad-radio-field / mad-dbradio-field) | form->setItems($name, $items) | reload_radio |
| Options de um checkbox-group | form->setItems($name, $items) | reload_checkbox_group |
| Options de um multi-entry (tags) | form->setItems($name, $items) | reload_multi_entry |
| Items de um sort-list | form->setItems($name, $items) | reload_sort_list |
| Items de um checklist | form->setItems($name, $items) | reload_checklist |
| Carregar options de combo via Eloquent (sem filtro) | form->loadOptionsFromModel($name, ...) | reload_combo |
Field-list (linhas dinâmicas) — convenção nome[]
| Preciso atualizar... | Método | Op gerada |
|---|---|---|
Valor de campo de uma row (no mad:fl-change) | form->set('campo[]', $v) | fl_val |
Options de combo de uma row (no mad:fl-change) | form->setItems('campo[]', $items) | fl_combo |
| Substituir todas as rows do field-list | form->setRows('nome', $rows) | fl_rows |
Ops fl_val/fl_combo só funcionam dentro de actions
disparadas por mad:fl-change num <mad-field-list-column>
(atributo on-change) — o JS captura
row = el.closest('.mad-fl-row') antes do POST e
patcha somente essa row na resposta. Outras actions (botão global, submit) não
têm contexto de row e essas ops ficam órfãs.
Ambos podem ser chamados em sequência na mesma action — as ops são mescladas. Quando a
action só muda estado, retorne void. Quando precisa de toast/drawer/close,
retorne MadResponse e o framework funde com os ops auto-gerados.
$this->form->set($field, $value) — mudar valor de campo
$this->form->set(string $field, mixed $value): void;
// Preencher campos ao selecionar uma pessoa
public function onPessoaChange(string $value): void
{
$p = Pessoa::find($value);
$this->form->set('pessoa_nome', $p->nome ?? '');
$this->form->set('pessoa_email', $p->email ?? '');
$this->form->set('pessoa_telefone', $p->telefone ?? '');
}
// Com MadResponse mesclado (toast + val)
public function onConfirmar(): MadResponse
{
$this->form->set('status', 'Confirmado');
return MadToast::success('Status atualizado!');
}
Como funciona
set()escreve em$this->form->fields[$field]e invalida o cache degetData().- Ao final da action,
MadComponentHandlerserializa oMadForme compara com o snapshot pré-action. - Para cada campo escalar mudado, emite
['op' => 'val', 'target' => '[name="X"]', 'content' => ...]. - O JS aplica:
el.value = ...; dispatchEvent(change)— notifica Alpine/MAD Select.
$this->form->setItems(...) — mudar options de coleção
$this->form->setItems(
string $field,
array $items, // mapa [value => label]
string|array|null $selected = null, // valor(es) pré-selecionado(s)
?string $placeholder = null // só para combo
): void;
// Combo
$this->form->setItems('unit_id', ['1' => 'Matriz', '2' => 'Filial'], '1');
// Combo com placeholder
$this->form->setItems('cidade_id', $cidades, null, '— Selecione —');
// Radio
$this->form->setItems('tipo', ['A' => 'Ativo', 'I' => 'Inativo'], 'A');
// Checkbox group (multi-select)
$this->form->setItems('perms', ['read' => 'Ler', 'write' => 'Escrever'], ['read']);
// Multi-entry / tags
$this->form->setItems('tags', $tagOptions, ['urgente', 'bug']);
// Sort list (a ordem do $selected define a ordem inicial)
$this->form->setItems('ordem_colunas', $campos, ['nome', 'email', 'id']);
// Checklist
$this->form->setItems('grupos', $grupos, ['1', '3']);
Cascata de combos
public function onEstadoChange(string $estadoId): void
{
$cidades = Cidade::where('estado_id', $estadoId)
->orderBy('nome')
->pluck('nome', 'id')
->all();
$this->form->setItems('cidade_id', $cidades, null, '— Selecione —');
}
Múltiplos campos numa action (tudo parcial, 1 request)
public function onCarregarTudo(): MadResponse
{
$this->form->setItems('estado_id', $estados, null, '—');
$this->form->setItems('tipo', $tipos, 'A');
$this->form->setItems('permissoes', $perms, ['read']);
$this->form->setItems('tags', $tagOpts);
return MadToast::success('Tudo populado!');
// Resultado: 4 ops reload_* + 1 op toast — tudo mesclado, sem re-render.
}
Como funciona
setItems()guarda as options em$this->form->items[$field]e (opcional) atualiza$this->form->fields[$field]com o(s) valor(es) selecionado(s).- Os dois buckets (
fields,items) são serializados nomad_state. - No diff pós-action,
MadComponentHandlercomparaitemsantes × depois. - Para cada campo com items alterados, consulta o tipo registrado em
MadFormRegistry::getFields()(preenchido durante o render) e emite a opreload_*correspondente. - O handler JS aplica: combo via options nativas, radio/checkbox-group/sort-list via reconstrução do DOM, multi-entry via API do MAD Select (
el._madSelect), checklist via Alpine data.
Padrão recomendado em actions
// Action retorna void — só muda estado
public function onFiltrar(): void
{
$this->form->set('status', 'ativo');
$this->form->setItems('categoria_id', $this->getCategorias());
}
// Action retorna MadResponse — estado + op explícita
public function onSalvar(): MadResponse
{
$this->form->set('status', 'Salvo');
return MadToast::success('Registro salvo!');
}
Armadilhas — NUNCA fazer
1. Não espelhe items numa prop pública do componente
Props array públicas que mudam durante a action e não têm
entry no MadVarRegistry forçam full re-render (fallback
seguro). Full re-render recria o DOM, o Alpine reinicializa e
ops posteriores como openModal não são ouvidas pela nova
instância.
// ERRADO — força full re-render, destrói o modal antes do openModal ser ouvido
public array $unitOptions = [];
public function onLogin(): MadResponse
{
$this->unitOptions = ['1' => 'Matriz', '2' => 'Filial'];
$this->form->setItems('unit_id', $this->unitOptions);
return (new MadResponse())->openModal('login-unit'); // não funciona!
}
// CERTO — items só no form, sem prop array espelhada
public function onLogin(): MadResponse
{
$unitOptions = ['1' => 'Matriz', '2' => 'Filial']; // variável local
$this->form->setItems('unit_id', $unitOptions);
return (new MadResponse())->openModal('login-unit');
}
Na view, o campo começa com :items="[]"; o reload_combo popula no client:
<mad-select-field name="unit_id" :items="[]" />
2. Não use MadResponse->val() quando form->set() resolve
// ERRADO — verboso, ignora o auto-bind
return (new MadResponse())->val('[name="nome"]', $nome);
// CERTO
$this->form->set('nome', $nome);
3. Não use html() manual pra popular options
// ERRADO — HTML injection, sem escape correto, sem sincronizar o MAD Select
$html = '';
foreach ($cidades as $id => $nome) {
$html .= "<option value=\"{$id}\">{$nome}</option>";
}
return (new MadResponse())->html('select[name="cidade_id"]', $html);
// CERTO
$this->form->setItems('cidade_id', $cidades);
4. Não chame reloadCombo explícito quando setItems resolve
// Verboso — funciona, mas não é o padrão
return (new MadResponse())->reloadCombo('cidade_id', $cidades, null, '—');
// Preferido — auto-bind via setItems, action pode retornar void
$this->form->setItems('cidade_id', $cidades, null, '—');
5. Não esqueça o campo de destino no Blade
Se o Blade não declara um <mad-select-field name="cidade_id">,
o MadFormRegistry não registra o tipo, o diff do handler não sabe qual op
emitir, e cai no fallback de full re-render. Sempre declare o campo na view, mesmo que
inicialmente vazio.
<!-- Campo declarado mesmo vazio — o tipo é registrado no schema -->
<mad-select-field name="cidade_id" label="Cidade" :items="[]" />
6. Não use form->setItems com componentes não suportados
A tabela do início desta página é autoritativa. Outros componentes (input, date,
number, file, image, etc.) não têm bucket de items, e
setItems não faz nada útil neles. Use form->set() para
valores escalares.
Field-list — auto-bind por linha (convenção nome[])
MadForm reconhece o sufixo [] no nome do campo como
field-list e emite ops escopadas à row de origem do change event.
Como o roteamento por row funciona
O JS captura row = el.closest('.mad-fl-row') antes do POST
e patcha somente essa row na resposta. Por isso, ops fl_val/fl_combo
só funcionam quando a action é disparada por mad:fl-change
(atributo on-change numa <mad-field-list-column>).
form->set('campo[]', $valor) — valor na row atual
public function onChangeProduto($value): void
{
$p = Produto::find($value);
$this->form->set('valor[]', $p->preco ?? 0);
$this->form->set('unidade[]', $p->unidade ?? '');
$this->form->set('descricao[]', $p->descricao ?? '');
}
form->setItems('campo[]', $items) — combo da row atual
public function onChangeFamilia($value): void
{
$produtos = Produto::where('familia_id', $value)
->orderBy('nome')
->pluck('nome', 'id')
->all();
$this->form->setItems('produto_id[]', $produtos);
}
form->loadOptionsFromModel('campo[]', ...) — atalho via Eloquent
public function onChangeTipo($value): void
{
// loadOptionsFromModel($field, $model, $key='id', $display='nome', $orderBy=null)
// — não tem parâmetro de filtro/WHERE. Para filtrar, monte a query você
// mesmo e use setItems() (ver onChangeFamilia acima), ou
// ModelOptionsLoader::itemsFromQuery() com um builder já filtrado.
$this->form->loadOptionsFromModel('produto_id[]', \App\Models\Produto::class, 'id', 'nome');
}
Funciona idêntico no form principal — sem [] emite reload_combo (serializado no state), com [] emite fl_combo (escopo de row).
form->setRows('nome', $rows) — substitui todas as rows
Diferente das outras ops de field-list, setRows não depende de contexto de row — pode ser chamado em qualquer action:
public function onCarregarParcelas(): void
{
$rows = Parcela::where('pedido_id', $this->pedidoId)
->orderBy('numero')
->get();
$this->form->setRows('parcelas', $rows);
}
form->set vs FieldListColumn::setValue
| Cenário | Usar |
|---|---|
Action retorna void (só muda estado) | $this->form->set('campo[]', $v) |
Precisa combinar com toast, openDrawer etc. | FieldListColumn::setValue('campo', $v) + merge() |
| Mistura form principal + field-list na mesma action | form->set (escolhe a op pelo sufixo []) |
FieldListColumn::* continua existindo e funcionando — usar quando precisar do retorno explícito MadResponse para encadear ops de UI.
NUNCA fazer
// ERRADO: usar form->set('campo[]') fora de mad:fl-change
// (botão global, submit, mad:click) — op fl_val fica órfã, sem row de destino
public function onSalvar(): MadResponse
{
$this->form->set('valor[]', 100); // não tem row de origem
return (new MadResponse())->closeDrawer();
}
// CERTO: form principal usa nome sem []
$this->form->set('valor', 100);
// CERTO: field-list usa setRows, que opera no field-list inteiro
$this->form->setRows('itens', $rows);
// ERRADO: misturar APIs no mesmo handler de fl-change
public function onChangeProduto($value): MadResponse
{
$this->form->set('valor[]', 12.50); // gera _pendingFlOps
return FieldListColumn::setValue('unidade', 'UN'); // outro fluxo
// → o pendingFlOps some — o return abandona o caminho void
}
// CERTO: tudo via form->set, return void
public function onChangeProduto($value): void
{
$this->form->set('valor[]', 12.50);
$this->form->set('unidade[]', 'UN');
}
// CERTO ALTERNATIVO: tudo via FieldListColumn + merge
public function onChangeProduto($value): MadResponse
{
return FieldListColumn::setValue('valor', 12.50)
->merge(FieldListColumn::setValue('unidade', 'UN'));
}
Quando MadResponse explícito ainda é necessário
- Abrir/fechar modal ou drawer:
openModal,closeModal,openDrawer,closeDrawer - Toast/alert:
toast,MadToast::*,MadMessage::error - Redirect / navigate:
redirect(...),redirectUrl(...) - Remover/atualizar linha de grid:
removeRow,manageRow - Elementos fora de um form MAD (ex: popular um
<div>com conteúdo):html(selector, content) - Recarregar transporter:
energize(name)
Nesses casos, combine livremente com set/setItems:
public function onSave(): MadResponse
{
$this->form->set('ultima_alteracao', date('d/m/Y H:i'));
$this->form->setItems('historico', $this->loadHistorico());
return (new MadResponse())
->toast('Salvo!', 'success')
->closeDrawer();
}