Estrutura de formulário
mad-field-list
mad-field-list-column
mad-field-list-group
mad-field-list-action
Básico (modo virtual)
mad-field-list sem model/foreign-key — linhas ad-hoc com compute e soma no rodapé.
Descrição
*
Qtd
Valor unit.
Total
Nenhum item adicionado
R$
R$
(/0)
Modo virtual — sem model/foreign-key: as linhas vivem só no estado do formulário.
resources/views/tutor/examples/estrutura-field-list/basico.blade.php
@php
// O `=>` do array PHP inline quebrava o parser de tag do
// mad-field-list: o regex que localiza a tag-mãe (MadGridCompiler)
// não é ciente de aspas (ao contrário do regex dos filhos
// mad-field-list-column, que já leva esse cuidado) e termina no
// primeiro '>' que encontrar — inclusive o de dentro de "=>" num
// :rows="[...]" inline. Resultado: a tag "fecha" no meio do array, o
// resto vira "corpo descartado" e :rows chega como boolean `true` em
// vez do array — e o aviso de diagnóstico do MadGridCompiler (que cita
// "@foreach" na própria mensagem) quebra a compilação Blade da página
// inteira. Workaround: array fora da tag, referenciado por variável
// (sem "=>"/"->" no atributo).
$rowsBasico = [
['descricao' => 'Mouse sem fio', 'qtd' => 2, 'valor_unit' => 89.90, 'total' => 179.80],
['descricao' => 'Teclado mecânico', 'qtd' => 1, 'valor_unit' => 349.00, 'total' => 349.00],
];
@endphp
<mad-field-list name="itens_basico" addable removable add-label="Adicionar item"
hint="Modo virtual — sem model/foreign-key: as linhas vivem só no estado do formulário."
:rows="$rowsBasico">
<mad-field-list-column field="descricao" label="Descrição" type="text" required
placeholder="Descrição do item" />
<mad-field-list-column field="qtd" label="Qtd" type="numeric" width="90px" default="1" />
<mad-field-list-column field="valor_unit" label="Valor unit." type="money" width="130px" prefix="R$" />
<mad-field-list-column field="total" label="Total" type="money" width="130px" prefix="R$"
compute="{qtd} * {valor_unit}" readonly sum />
</mad-field-list>
Grupos e tipos de coluna
mad-field-list-group agrupando colunas select, date e money.
Identificação
Comercial
Descrição
*
Categoria
Validade
Valor
Nenhum item adicionado
DSTQQSS
R$
(/0)
resources/views/tutor/examples/estrutura-field-list/grupos-tipos.blade.php
@php
// Array fora da tag (referenciado por variável): `=>` inline no
// :rows="[...]" quebra o parser da tag-mãe mad-field-list
// (MadGridCompiler) — ver o comentário completo em
// examples/estrutura-field-list/basico.blade.php.
$rowsGrupos = [
['descricao' => 'Cadeira ergonômica', 'categoria' => 'moveis', 'validade' => '2027-01-15', 'valor' => 890.00],
['descricao' => 'Resma de papel A4', 'categoria' => 'papelaria', 'validade' => '2026-06-30', 'valor' => 24.90],
];
@endphp
<mad-field-list name="itens_grupos" addable removable add-label="Adicionar item"
:rows="$rowsGrupos">
<mad-field-list-group label="Identificação">
<mad-field-list-column field="descricao" label="Descrição" type="text" required />
<mad-field-list-column field="categoria" label="Categoria" type="select"
:options="['eletronico' => 'Eletrônico', 'papelaria' => 'Papelaria', 'moveis' => 'Móveis']" />
</mad-field-list-group>
<mad-field-list-group label="Comercial">
<mad-field-list-column field="validade" label="Validade" type="date" />
<mad-field-list-column field="valor" label="Valor" type="money" prefix="R$" sum />
</mad-field-list-group>
</mad-field-list>
Coluna ligada a banco + on-change
type=dbcombo lendo TutorDemoCliente; on-change preenche outra coluna da mesma linha.
Cliente
Cidade/UF
Nenhum item adicionado
(/0)
Selecionar um cliente preenche Cidade/UF na mesma linha (on-change chama form-set no servidor).
resources/views/tutor/examples/estrutura-field-list/dbcombo-reativo.blade.php
<mad-field-list name="itens_reativo" addable removable add-label="Adicionar cliente"
hint="Selecionar um cliente preenche Cidade/UF na mesma linha (on-change chama form-set no servidor).">
<mad-field-list-column field="cliente_id" label="Cliente" type="dbcombo"
model="TutorDemoCliente" database="business" display="{nome}"
on-change="onClienteFieldListChange" />
<mad-field-list-column field="cidade_uf" label="Cidade/UF" type="text" readonly
placeholder="Selecione um cliente..." />
</mad-field-list>
app/control/Tutor/TutorEstruturaFieldList.php
/**
* on-change da coluna dbcombo "cliente_id" (exemplo "dbcombo-reativo").
* Recebe o novo valor selecionado e preenche "cidade_uf" na MESMA linha
* via form->set('campo[]', ...) — op fl_val, escopada à row de origem do
* mad:fl-change (framework resolve o parâmetro $value por nome).
*/
public function onClienteFieldListChange($value = null): MadResponse
{
$cliente = $value !== null && $value !== ''
? TutorDemoCliente::query()->find($value)
: null;
$cidadeUf = $cliente ? trim($cliente->cidade . '/' . $cliente->uf, '/') : '';
$this->form->set('cidade_uf[]', $cidadeUf);
return new MadResponse();
}
Ações de linha
mad-field-list-action dispara método PHP que duplica a linha via form->setRows().
Descrição
*
Qtd
Valor unit.
Nenhum item adicionado
R$
(/0)
resources/views/tutor/examples/estrutura-field-list/acoes-linha.blade.php
@php
// Array fora da tag (referenciado por variável): `=>` inline no
// :rows="[...]" quebra o parser da tag-mãe mad-field-list
// (MadGridCompiler) — ver o comentário completo em
// examples/estrutura-field-list/basico.blade.php.
$rowsAcoes = [
['descricao' => 'Suporte para monitor', 'qtd' => 1, 'valor_unit' => 159.90],
['descricao' => 'Hub USB-C', 'qtd' => 3, 'valor_unit' => 79.90],
];
@endphp
<mad-field-list name="itens_acoes" addable removable add-label="Adicionar item"
:rows="$rowsAcoes">
<mad-field-list-column field="descricao" label="Descrição" type="text" required />
<mad-field-list-column field="qtd" label="Qtd" type="numeric" width="90px" default="1" />
<mad-field-list-column field="valor_unit" label="Valor unit." type="money" width="130px" prefix="R$" />
<mad-field-list-action method="onDuplicarLinhaDemo" icon="copy" title="Duplicar linha" />
</mad-field-list>
app/control/Tutor/TutorEstruturaFieldList.php
/**
* mad-field-list-action "Duplicar" (exemplo "acoes-linha"). A linha
* clicada chega como parâmetros nomeados (merge automático row+params
* antes do Mad.call); form->setRows() substitui TODAS as linhas do
* field-list — ao contrário de set()/setItems(), não depende de contexto
* de row, então funciona numa action de botão comum.
*/
public function onDuplicarLinhaDemo(string $descricao = '', $qtd = null, $valor_unit = null): MadResponse
{
$rows = $this->form->getFieldList('itens_acoes');
$rows[] = [
'descricao' => trim($descricao) !== '' ? $descricao . ' (cópia)' : 'Item (cópia)',
'qtd' => $qtd,
'valor_unit' => $valor_unit,
];
$this->form->setRows('itens_acoes', $rows);
return (new MadResponse())
->toast("Linha duplicada: {$descricao}", 'success', 'onDuplicarLinhaDemo');
}
Ação wire
mad-form com submit real — dispara TutorEstruturaFieldList::onSubmitFieldListDemo.
resources/views/tutor/examples/estrutura-field-list/wire-submit.blade.php
@php
// Array fora da tag (referenciado por variável): `=>` inline no
// :rows="[...]" quebra o parser da tag-mãe mad-field-list
// (MadGridCompiler) — ver o comentário completo em
// examples/estrutura-field-list/basico.blade.php.
$rowsWire = [
['descricao' => 'Cabo HDMI 2m', 'qtd' => 2, 'valor_unit' => 39.90, 'total' => 79.80],
];
@endphp
<mad-form submit="onSubmitFieldListDemo">
<mad-field-list name="itens_wire" addable removable add-label="Adicionar item"
:rows="$rowsWire">
<mad-field-list-column field="descricao" label="Descrição" type="text" required />
<mad-field-list-column field="qtd" label="Qtd" type="numeric" width="90px" default="1" />
<mad-field-list-column field="valor_unit" label="Valor unit." type="money" width="130px" prefix="R$" />
<mad-field-list-column field="total" label="Total" type="money" width="130px" prefix="R$"
compute="{qtd} * {valor_unit}" readonly sum />
</mad-field-list>
<mad-form-actions align="right">
<mad-btn type="submit" variant="primary" icon="send">Enviar (onSubmitFieldListDemo)</mad-btn>
</mad-form-actions>
</mad-form>
app/control/Tutor/TutorEstruturaFieldList.php
/**
* Submit do exemplo "Ação wire" — lê as linhas via getFieldList() (modo
* virtual, sem persistência) e devolve um toast com o resumo do payload.
*/
public function onSubmitFieldListDemo(): MadResponse
{
$itens = $this->form->getFieldList('itens_wire');
$qtdTotal = 0.0;
$valorTotal = 0.0;
foreach ($itens as $row) {
$qtd = (float) ($row['qtd'] ?? 0);
$qtdTotal += $qtd;
$valorTotal += $qtd * (float) ($row['valor_unit'] ?? 0);
}
$resumo = count($itens) . ' linha(s), qtd total ' . rtrim(rtrim(number_format($qtdTotal, 2, ',', '.'), '0'), ',')
. ', valor total R$ ' . number_format($valorTotal, 2, ',', '.');
return (new MadResponse())
->toast("Field list recebido: {$resumo}", 'success', 'onSubmitFieldListDemo');
}
Código-fonte completo da página
app/control/Tutor/TutorEstruturaFieldList.php
<?php
namespace App\Control\Tutor;
use App\Models\TutorDemo\TutorDemoCliente;
use App\Support\Tutor\TutorDemoSeed;
use App\Support\Tutor\TutorPage;
use Mad\Form\MadForm;
use Mad\Http\MadResponse;
/**
* TutorEstruturaFieldList — cobre <mad-field-list>, <mad-field-list-column>,
* <mad-field-list-group> e <mad-field-list-action>.
*
* Todos os exemplos usam field-list em MODO VIRTUAL (sem model/foreign-key na
* tag): nenhuma linha é persistida em banco — on-change, ação de linha e o
* submit final só leem o payload recebido e devolvem toast (nível demo).
*/
class TutorEstruturaFieldList extends TutorPage
{
protected string $catalogKey = 'estrutura_field_list';
public MadForm $form;
public function mount(array $params = []): void
{
TutorDemoSeed::ensure();
$this->form = new MadForm('tutor_estrutura_field_list');
}
/**
* on-change da coluna dbcombo "cliente_id" (exemplo "dbcombo-reativo").
* Recebe o novo valor selecionado e preenche "cidade_uf" na MESMA linha
* via form->set('campo[]', ...) — op fl_val, escopada à row de origem do
* mad:fl-change (framework resolve o parâmetro $value por nome).
*/
public function onClienteFieldListChange($value = null): MadResponse
{
$cliente = $value !== null && $value !== ''
? TutorDemoCliente::query()->find($value)
: null;
$cidadeUf = $cliente ? trim($cliente->cidade . '/' . $cliente->uf, '/') : '';
$this->form->set('cidade_uf[]', $cidadeUf);
return new MadResponse();
}
/**
* mad-field-list-action "Duplicar" (exemplo "acoes-linha"). A linha
* clicada chega como parâmetros nomeados (merge automático row+params
* antes do Mad.call); form->setRows() substitui TODAS as linhas do
* field-list — ao contrário de set()/setItems(), não depende de contexto
* de row, então funciona numa action de botão comum.
*/
public function onDuplicarLinhaDemo(string $descricao = '', $qtd = null, $valor_unit = null): MadResponse
{
$rows = $this->form->getFieldList('itens_acoes');
$rows[] = [
'descricao' => trim($descricao) !== '' ? $descricao . ' (cópia)' : 'Item (cópia)',
'qtd' => $qtd,
'valor_unit' => $valor_unit,
];
$this->form->setRows('itens_acoes', $rows);
return (new MadResponse())
->toast("Linha duplicada: {$descricao}", 'success', 'onDuplicarLinhaDemo');
}
/**
* Submit do exemplo "Ação wire" — lê as linhas via getFieldList() (modo
* virtual, sem persistência) e devolve um toast com o resumo do payload.
*/
public function onSubmitFieldListDemo(): MadResponse
{
$itens = $this->form->getFieldList('itens_wire');
$qtdTotal = 0.0;
$valorTotal = 0.0;
foreach ($itens as $row) {
$qtd = (float) ($row['qtd'] ?? 0);
$qtdTotal += $qtd;
$valorTotal += $qtd * (float) ($row['valor_unit'] ?? 0);
}
$resumo = count($itens) . ' linha(s), qtd total ' . rtrim(rtrim(number_format($qtdTotal, 2, ',', '.'), '0'), ',')
. ', valor total R$ ' . number_format($valorTotal, 2, ',', '.');
return (new MadResponse())
->toast("Field list recebido: {$resumo}", 'success', 'onSubmitFieldListDemo');
}
}
resources/views/tutor/estrutura/field-list.blade.php
<x-tutor-page page="estrutura_field_list">
<x-tutor-demo demo="tutor.examples.estrutura-field-list.basico"
title="Básico (modo virtual)" desc="mad-field-list sem model/foreign-key — linhas ad-hoc com compute e soma no rodapé." />
<x-tutor-demo demo="tutor.examples.estrutura-field-list.grupos-tipos"
title="Grupos e tipos de coluna" desc="mad-field-list-group agrupando colunas select, date e money." />
<x-tutor-demo demo="tutor.examples.estrutura-field-list.dbcombo-reativo"
title="Coluna ligada a banco + on-change" desc="type=dbcombo lendo TutorDemoCliente; on-change preenche outra coluna da mesma linha."
php="TutorEstruturaFieldList::onClienteFieldListChange" />
<x-tutor-demo demo="tutor.examples.estrutura-field-list.acoes-linha"
title="Ações de linha" desc="mad-field-list-action dispara método PHP que duplica a linha via form->setRows()."
php="TutorEstruturaFieldList::onDuplicarLinhaDemo" />
<x-tutor-demo demo="tutor.examples.estrutura-field-list.wire-submit"
title="Ação wire" desc="mad-form com submit real — dispara TutorEstruturaFieldList::onSubmitFieldListDemo."
php="TutorEstruturaFieldList::onSubmitFieldListDemo" />
</x-tutor-page>