Docs›Formulários›Regras do Blade MAD
Formulários

Regras do Blade MAD

Compilador MAD (BladeCompiler nativo do Illuminate + pré-processo <mad-*>): named slots, @props, escape de aspas em atributos.

O MAD compila views em duas camadas: primeiro o MadGridCompiler reescreve tags <mad-xxx> em texto cru (regex sobre o arquivo, antes de qualquer parser), depois o MadBladeCompiler compila o resultado como Blade nativo. Essa combinação tem limitações específicas que não existem no Blade puro do Laravel — esta página documenta as que mais geram bug silencioso.

Traduções i18n com __()

Três contextos, três formas diferentes de injetar texto traduzido — misturar gera bug.

ContextoForma
Atributo de componente (label="...", title="...")Bind PHP: :label="__('...')"
Conteúdo/slot de componente (entre <mad-btn>...</mad-btn>)Raw echo: {!! __('...') !!}
HTML puro, fora de componenteEcho normal: {{ __('...') }}
<!-- Atributo de componente: bind PHP -->
<mad-input-field :label="__('doc.recipe_name')" />
<mad-form-section :title="__('doc.ingredients')" icon="list">

<!-- Conteúdo/slot de componente: raw echo -->
<mad-btn variant="primary">{!! __('mad.save') !!}</mad-btn>

<!-- HTML puro, fora de componente: echo normal -->
<option value="">{{ __('doc.select_recipe') }}</option>
<h1>{{ __('doc.recipe') }}</h1>
NUNCA {{ __() }} em atributo de componente

{{ }} aplica htmlentities(), que escapa aspas simples. Num atributo de componente isso produz algo como label="Traduç&#039;ão" em vez do texto correto — o componente recebe lixo, não o valor traduzido.

<!-- ERRADO — @{{ }} escapa aspas, quebra o valor -->
<mad-input-field label="{{ __('doc.recipe_name') }}" />
<mad-form-section title="{{ __('doc.details') }}" />

<!-- CERTO — :prop com bind PHP, sem escape de entidades -->
<mad-input-field :label="__('doc.recipe_name')" />
<mad-form-section :title="__('doc.details')" />

<!-- CERTO para valores fixos sem aspas no meio -->
<mad-input-field label="Nome" />
<mad-form-section title="Dados" />

Tags compiladas pelo MadGridCompiler

Tags como <mad-grid>, <mad-col>, <mad-act>, <mad-detail-form>, <mad-field-list-column> são processadas por regex antes do MadBladeCompiler — não são componentes Blade nativos. Expressões PHP nesses atributos sempre usam o prefixo ::

<mad-col field="nome" :label="__('doc.recipe_name')" sort />
<mad-detail-form name="itens" :form-title="__('doc.ingredient')" mode="drawer" />

Bug MadBladeCompiler — @if como primeiro filho de outro componente

O MadBladeCompiler falha ao compilar um <x-component> (incluindo os resultantes da reescrita mad-xxx → x-xxx) quando ele é o primeiro filho de outro componente e é precedido por @if. Os atributos :prop="expr" passam direto para o HTML sem ser avaliados, e o Alpine tenta interpretar a string crua como JS.

<!-- ERRADO — <mad-form-section> não compila, :title vira atributo HTML literal -->
<mad-form submit="onSave">
    @if($showFile)
    <mad-form-section :title="__('ged.upload')" icon="upload">
        <mad-file-field :label="__('ged.filename')" />
    </mad-form-section>
    @endif
</mad-form>

<!-- CERTO — mover o @if para FORA do <mad-form>, ou o form inteiro para dentro do @if -->
@if($showFile)
<mad-form submit="onSave">
    <mad-form-section :title="__('ged.upload')" icon="upload">
        <mad-file-field :label="__('ged.filename')" />
    </mad-form-section>
</mad-form>
@endif

Regra geral: nunca use @if como primeiro filho direto dentro de <mad-form> ou de qualquer outro componente Blade. Alternativas:

  1. Mover o @if para fora do componente pai.
  2. Inserir um <div> wrapper entre o componente pai e o @if (menos elegante, mas funciona).
  3. Separar em blocos condicionais independentes.
<!-- ALTERNATIVA: wrapper div -->
<mad-form submit="onSave">
    <div>
        @if($showSection)
        <mad-form-section :title="$titulo">...</mad-form-section>
        @endif
    </div>
</mad-form>

Regras gerais do MadBladeCompiler (limitações vs. Blade nativo do Laravel)

  1. @props funciona — o MadBladeCompiler registra a diretiva (declare defaults no topo do componente: @props(['size' => 'md', 'required' => false])). O que fica desligado é só o ComponentTagCompiler de componentes-classe do Laravel — os componentes anônimos <x-...>/<mad-...> recebem props normalmente.
  2. Named slots <x-slot name="..."> são suportados — o compilador MAD extrai a tag e converte em slot clássico (o antigo bug "Template not found: components.slot" foi corrigido). Para valores simples, uma prop costuma ser mais direta que um slot:
    <!-- OK — named slot via tag -->
    <mad-modal name="meu-modal" size="md">
        <x-slot name="footer"><button class="mad-btn">OK</button></x-slot>
        Conteúdo...
    </mad-modal>
    
    <!-- Também OK, mais direto p/ valor simples — título como prop -->
    <mad-modal name="meu-modal" :title="'Meu Título'" size="md">
        Conteúdo...
    </mad-modal>
  3. {!! $slot !!} dentro de componentes — nunca {{ $slot }} (escaparia o HTML do conteúdo).
  4. Strings multi-palavra em atributos — pré-definir num @php e passar com :prop="$var", em vez de tentar montar inline.
  5. Cache de template — limpe tmp/blade-cache/*.bladec (ou rode php artisan view:clear) ao mudar um template e a alteração não aparecer.
  6. attrs com aspas duplas internas — pré-compute no @php: $at = 'data-x="val"'; e passe com :attrs="$at" (ver também Aspas em atributos).
  7. ?? em props — o MadBladeCompiler não faz parse de :prop="$arr['key'] ?? ''" direto no atributo. Pré-compute no @php.
  8. Sem @component/@endcomponent — essa diretiva não é registrada. Use <x-component> (ou <mad-xxx>) ou PHP direto.
  9. mad:model obrigatório em campos de formulário — todo componente de campo (mad-input-field, mad-select-field, etc.) precisa garantir que mad:model chegue no <input>/<select> renderizado mesmo quando $attrs já contém outros atributos (ex: mad:change). Sem mad:model, o MadWire não coleta o valor do campo no POST:
    <!-- ERRADO — mad:model só é adicionado quando $attrs está vazio -->
    if ($name && $attrs === '') {
        $attrs = 'mad:model="' . $name . '"';
    }
    
    <!-- CERTO — verifica se mad:model já existe antes de adicionar -->
    if ($name && strpos($attrs, 'mad:model') === false && strpos($attrs, 'data-mad-model') === false) {
        $attrs = 'mad:model="' . $name . '" ' . $attrs;
    }
  10. Sem <form> nativo — nunca escreva <form data-mad-submit="..."> à mão. Sempre <mad-form submit="metodo"> (ele gera o <form> real por baixo, incluindo o token de schema):
    <!-- ERRADO -->
    <form data-mad-submit="onSave">...</form>
    
    <!-- CERTO -->
    <mad-form submit="onSave">...</mad-form>

Sync especial de componentes em mad-detail-form

O madDetailForm (Alpine) sincroniza o DOM com o estado interno em alguns componentes que mantêm seu próprio Alpine state — esses precisam de tratamento especial além de value/x-model simples:

ComponenteSync necessário
mad-numeric-fieldSetar Alpine.$data().rawValue + atualizar o display visível
mad-dbseek-fieldSetar Alpine.$data().selectedId + displayText
MAD Select (select, dbcombo, multi-search)Usar el._madSelect.setValue(val, true) — widget JS próprio do framework, não é TomSelect nem outra lib de terceiros
mad-html-editor-fieldQuill clipboard.dangerouslyPasteHTML()
mad-checklist-fieldSetar array checkedIds + checkboxes individuais

Componentes que não precisam de sync especial (o value direto já funciona): input-field, textarea-field, number-field, date-field, time-field, switch-field, checkbox-field, radio-field, range-field, color-field.

Próximos