mad-multi-search-field
Multi-select com chips + busca.
Select multiplo com busca client-side (chips) sobre options ja carregadas em memoria (:options). Suporta dois modos de persistencia: comma (CSV numa coluna) ou table (tabela pivot 1:N).
Nao confundir com
<mad-dbmulti-search-field>— esse e um componente DIFERENTE (verdbmulti-search.md), que faz busca AJAX server-side no banco viamodel/display(sem precisar de:optionspre-carregadas).<mad-multi-search-field>nao tem propmodel/display/query— so aceita options ja prontas.Para texto livre (sem selecao de lista), ver
search-field.md. Para selecao unica com busca, verunique-search.md.
Props
| Prop | Tipo | Default | Descricao |
|---|---|---|---|
| name | string | '' | Nome do campo (enviado como name[]) |
| label | string | '' | Label |
| width | string | '' | Largura inline |
| max-width | string | '' | Largura maxima inline |
| options | array | [] | [id => label, ...] |
| selected | array/string | [] | IDs pre-selecionados (array ou CSV). Auto-resolvido do MadRenderContext quando vazio |
| placeholder | string | 'Selecione...' | |
| min-length | int | 0 | Caracteres minimos antes de filtrar |
| max-size | int | 0 | Maximo de itens (0 = ilimitado) |
| mode | string | 'comma' | comma (CSV na coluna) ou table (pivot) |
| pivot-model | string | '' | Classe do model Eloquent da tabela pivot (mode=table) |
| foreign-key | string | '' | Coluna FK do registro pai (mode=table) |
| item-key | string | '' | Coluna FK do item selecionado (mode=table) |
| database | string | MAIN_DATABASE (fallback business) |
Conexao usada para carregar/gravar o pivot (mode=table). Sem a constante MAIN_DATABASE definida, cai em business |
| hint | string | '' | Texto de ajuda |
| error | string | '' | Mensagem de erro |
| required | bool | false | |
| disabled | bool | false | |
| attrs | string | '' | Atributos HTML extras |
| no-results-create-action | string | '' | Classe::metodo — abre form em drawer/modal quando a busca nao acha nada |
| no-results-create-label | string | 'Cadastrar novo' | |
| no-results-create-icon | string | 'plus' | Icone Lucide |
| no-results-create-class | string | 'mad-btn mad-btn-primary mad-btn-sm' | |
| no-results-quick-register-action | string | '' | Classe::metodoEstatico — cadastro inline sem sair do select |
| no-results-quick-register-label | string | 'Adicionar' | |
| no-results-quick-register-icon | string | 'check' | |
| no-results-quick-register-class | string | 'mad-btn mad-btn-success mad-btn-sm' | |
| no-results-quick-fields | array | [] | Campos extras exibidos no cadastro rapido (ver dbcombo-field.md) |
| no-results-message | string | '' | Texto exibido no topo do bloco de no-results |
mode, pivot-model, foreign-key, item-key e database nao tem default declarado no @props() do componente — sao atributos Blade passados direto e resolvidos com fallback (?? 'comma', etc.) no @php do componente (mesmo efeito pratico de uma prop).
Mode comma — CSV numa coluna
<mad-multi-search-field name="tags" label="Tags"
:options="$tagOptions" />
No banco: tags = '1,5,12'. O form->save() persiste como string. No onEdit, form->fill($record) distribui automaticamente.
Mode table — pivot 1:N
<mad-multi-search-field name="produtos" label="Produtos do pacote"
:options="$prodOptions"
mode="table"
pivot-model="PacoteProduto"
foreign-key="pacote_id"
item-key="produto_id" />
O form->save($pacote) faz delete all + insert na tabela pacote_produto automaticamente no _afterStore(). No onEdit, o componente carrega os IDs ja vinculados via MadRenderContext::loadPivotSelected() — nao precisa fazer nada manual.
Com max-size
<mad-multi-search-field name="categorias" label="Categorias"
:options="$catOptions" max-size="3"
placeholder="Ate 3 categorias" />
Evento change (PHP)
Aceita mad:change="metodo" via attrs. Atencao: como o campo e um <select multiple> nativo por baixo, o evento change do browser so expoe el.value, que para um select multiplo retorna apenas o valor da primeira <option> selecionada (limitacao do DOM, nao do MAD) — nao a lista completa. Para reagir a toda a selecao, leia o array completo a partir do form no submit ($this->form->getData()->tags), nao do mad:change.
<mad-multi-search-field name="tags" label="Tags"
:options="$tagOptions"
attrs='mad:change="onTagsChange"' />
public function onTagsChange(string $value): void
{
// $value e so a primeira option selecionada — uso tipico aqui e
// disparar um efeito colateral leve (ex: log, contador), nao ler
// a selecao inteira.
}
No-results: criar/cadastrar inline
Quando a busca nao encontra nada nas options carregadas, o componente pode mostrar um botao para abrir um form de cadastro (drawer/modal) ou um cadastro rapido inline — mesma API de no-results-* usada em todos os campos MAD Select do framework (unique-search, dbunique-search, dbmulti-search, dbcombo-field). Ver dbcombo-field.md para a API completa (formato do retorno do quickRegister, no-results-quick-fields, etc).
<mad-multi-search-field name="categorias" label="Categorias"
:options="$catOptions"
no-results-create-action="CategoriaForm::show"
no-results-quick-register-action="CategoriaForm::quickRegister"
no-results-message="Nao achou? Cadastre:" />
O quickRegister precisa ser metodo estatico publico que recebe ['term' => $nomeDigitado, 'field_name' => ..., ...] e retorna ['value' => $id, 'label' => $display].
Quando usar cada componente
| Cenario | Usar |
|---|---|
| Search box de filtro/listagem (texto livre) | <mad-search-field> (ver search-field.md) |
| Selecao unica com options em memoria | <mad-unique-search-field> (ver unique-search.md) |
| Selecao unica com busca AJAX no banco (lista grande) | <mad-dbunique-search-field> (ver dbunique-search.md) |
| Selecao multipla com options em memoria | <mad-multi-search-field> (esta pagina) |
| Selecao multipla com busca AJAX no banco (lista grande) | <mad-dbmulti-search-field> (ver dbmulti-search.md) |
| Selecao multipla salva como CSV em coluna | mode="comma" |
| Selecao multipla salva em tabela pivot | mode="table" |
| Busca com modal grid (varias colunas, paginacao) | <mad-seek> (ver seek.md) |
NUNCA fazer
{{-- ERRADO: select multiplo nativo com size --}}
<select name="tags[]" multiple size="5">
@foreach($tags as $id => $nome)
<option value="{{ $id }}">{{ $nome }}</option>
@endforeach
</select>
{{-- CERTO --}}
<mad-multi-search-field name="tags" :options="$tags" />
{{-- ERRADO: passar model/display esperando busca AJAX no banco --}}
<mad-multi-search-field name="produtos" model="Produto" display="nome" />
{{-- multi-search-field nao tem prop "model" — so aceita :options ja carregadas --}}
{{-- CERTO: usar dbmulti-search-field para busca AJAX no banco --}}
<mad-dbmulti-search-field name="produtos" model="Produto" display="nome" />
{{-- ERRADO: persistir multi manualmente em tabela pivot --}}
$this->form->save($pacote);
PacoteProduto::where('pacote_id', $pacote->id)->delete();
foreach ($_POST['produtos'] as $pid) { /* insert */ }
{{-- CERTO: declarar mode=table no Blade, save() faz tudo --}}
<mad-multi-search-field name="produtos" :options="$opts"
mode="table" pivot-model="PacoteProduto"
foreign-key="pacote_id" item-key="produto_id" />
{{-- ERRADO: widget de busca client-side manual --}}
<select id="meu-select" multiple class="mad-input"></select>
<script>/* inicializar lib de busca manualmente */</script>
{{-- CERTO --}}
<mad-multi-search-field name="campo" :options="$opts" />