Pular para o conteúdo principal

Fieldsets

Fieldsets são blocos de agrupamento usados para organizar visualmente e logicamente os campos, componentes e até outros fieldsets dentro de uma tela no Nerdify. Eles representam estruturas aninhadas e altamente configuráveis que controlam como a interface será montada no frontend.

📦 No frontend, um fieldset se comporta como um container dentro de um formulário, mas com recursos adicionais como cabeçalho, colapsamento e suporte a abas.


🧩 O que pode conter um fieldset

Um fieldset pode conter:

  • fieldsets filhos (aninhados)
  • fields (campos do modelo)
  • components (gráficos, botões, tabelas, cards, etc.)
  • embeds (relacionamentos aninhados)

Essa flexibilidade permite compor estruturas visuais complexas com organização modular.

🧠 A definição de como utilizar fields, embeds e components será explicada nas seções respectivas da documentação sobre cada um.


🔧 Parâmetros de configuração de um fieldset

ParâmetroTipoDescrição
nameSymbolNome do fieldset (obrigatório)
headerBooleanExibe um título no topo com o nome traduzido do fieldset
minifyBooleanPermite minimizar/expandir o conteúdo via caret
tabSymbolSe definido, o fieldset é transformado em uma aba
sizeIntegerNúmero de colunas no grid (1 a 12)
stylesHashEstilizações customizadas como background, padding, border, etc.
backend_ifStringCondição avaliada no backend. Ex: can?(:edit, object)
render_ifStringCondição avaliada no frontend. Se falsa, o bloco não é renderizado
show_ifStringCondição avaliada no frontend. Se falsa, o bloco é renderizado mas oculto

🔎 Entendendo as condições:

  • backend_if: → executado no backend com model, object, current_user, current_ability, etc.
  • render_if: → condição avaliada no frontend. Se falsa, o botão nem é renderizado.
  • show_if: → também é avaliada no frontend, mas apenas esconde o botão com hidden, mantendo o DOM.

🔗 Enquanto backend_if utiliza uma string que representa código ruby e tem acesso a recursos que existem no controlador, como object, model, params, entre outros, o render_if e show_if geram código na sintaxe javascript para o frontend usando dados que estão presentes na resposta json que monta a página com placeholeders, por exemplo, resource.id == page.resources.current_user.id

Você também pode passar qualquer outra chave customizada, que será incluída na estrutura final e poderá ser usada nos templates para lógica personalizada. Exemplo:

fieldset :documents, header: true, info: :test, custom_flag: true do
field :cpf
field :rg
end

Esses valores (info, custom_flag) podem ser utilizados nos templates customizados para lógica condicional ou estilização dinâmica.


🔀 Fieldsets personalizados por controlador

Você pode criar fieldsets que só aparecem em determinados controladores utilizando backend_if como critério condicional. Isso permite definir todas as possibilidades de uso do modelo em um só lugar (o arquivo do modelo), mantendo controle centralizado da regra de negócio e suas representações visuais.

Sem backend_if, o fieldset estará disponível em todos os controladores que utilizarem o modelo. Com backend_if, você pode limitar a exibição a determinados cenários, como no exemplo:

fieldset :demo, backend_if: "params[:controller] == 'admin/clientes'" do
field :demo_field
end

🎯 Isso evita a duplicação de lógica em várias telas/controladores e garante que qualquer modificação em campos ou agrupamentos possa ser feita no próprio modelo, com impacto direto e controlado nos lugares certos.

Essa abordagem também permite garantir que tudo sobre como utilizar o modelo esteja concentrado em um só lugar. Ou seja, mesmo que o modelo seja usado por diferentes controladores e fluxos, todas as variações de campos, agrupamentos e lógica visual podem ser declaradas no próprio modelo — com controle por backend_if quando necessário.


🔐 Segurança dos campos e proteção de parâmetros

Por padrão, os controladores do Nerdify utilizam os fieldsets para determinar quais atributos são aceitos em requisições. Isso significa que somente os campos definidos dentro de fieldsets serão permitidos para criação ou atualização de registros.

Existem duas formas de proteger campos:

  1. Fora de qualquer fieldset
fieldset :demo do
end
field :secret_field, type: String

Esses campos não serão visíveis em nenhuma interface nem aceitos em parâmetros de requisição — usados apenas internamente, como em callbacks ou métodos. Se você utilizar esta opção de definir o field fora do fieldset, você precisa usar a sintaxe de definição de campos do ORM utilizado, no caso, MongoID.

  1. Dentro do fieldset com protected: true
fieldset :demo do
field :secret_field, type: :string, protected: true
end

Nesse formato, você pode utilizar a sintaxe padrão da DSL do Nerdify.

Definindo o campo dentro do fieldset e usando protected, os campos serão visíveis, mas não poderão ser atualizados via requisição ou formulário do frontend. Eles serão ignorados mesmo que sejam passados como parametro na requisição e estejam presentes no formulário.

🔐 Essa proteção é baseada nos strong_parameters do Rails e evita ataques como mass assignment.

Você também pode proteger campos apenas em controladores específicos:

field :secret_field, type: :string, protected: { only: %w[admin/clientes] }
field :other_secret_field, type: :string, protected: { except: %w[admin/clientes] }

Além disso, é possível combinar backend_if com protected.

field :secret_field, type: :string, protected: { only: %w[public/customers] }, backend_if: "%w[admin/customers public/customers].include? params[:controller]"

No exemplo acima, o campo vai ser visível nos dois controladores, admin/customers e public/customers mas apenas no controlador admin/customers ele poderá ser alimentado ou atualizado.

🔐 Você deve ter cuidado ao utilizar backend_if para esconder campos que deveriam ser protegidos. O backend_if por si só apenas esconde, mas se o campo estiver dentro do fieldset, ele só será protegido a partir da definição dos valores do atributo protected.


🧪 Exemplo completo de fieldset com filhos

fieldset :customer, header: true, minify: true, size: 12 do
field :name, type: :string
field :email, type: :email

fieldset :addresses_info, tab: :address_title do
embed :addresses, include: true
end

fieldset :documents, tab: :documents do
component :text, :document_name, type: :span
end
end

🧭 Nesse exemplo, o fieldset customer terá um cabeçalho, será minimizável e conterá dois fieldsets filhos renderizados como abas (endereços e documentos). O nome da aba será traduzido automaticamente com base em tab: :address_title.


🧭 Form Wizard com Fieldsets

O Nerdify permite criar formulários em etapas (Form Wizard) utilizando apenas a configuração declarativa dentro dos próprios fieldsets.

Essa funcionalidade é ideal para dividir grandes formulários em múltiplas etapas visuais, com controle total sobre quais blocos aparecem em cada momento, de forma reativa e integrada com actions e callbacks do modelo.


🚦 Como configurar um Form Wizard

Você deve definir um fieldset pai com os parâmetros adicionais no fieldset, específicos para esta funcionalidade:

ParâmetroTipoDescrição
stepsArrayLista com os nomes das etapas (ex: %w[início dados resumo])
step_iconsArray(opcional) Ícones correspondentes a cada etapa (ex: %w[home info check])
fieldset :sale_form, steps: %w[step_name1 step_name2], step_icons: %w[icon_name1 icon_name2] do
# fieldsets filhos aqui
end

Cada fieldset filho deve incluir a opção form_wizard:, indicando por um número a qual etapa ele pertence.

fieldset :step1_name, form_wizard: 1 do
field :type
field :tipo
end

fieldset :step2_name, form_wizard: 2 do
field :value
field :quantity
end

🎨 Os fieldsets com form_wizard serão automaticamente organizados em um wizard visual com barra de progresso no frontend padrão do Nerdify.


⚙️ Avançando entre etapas com Actions

A transição entre etapas do form wizard é feita via actions.

Você deve configurar o params da action para informar a próxima etapa desejada, usando o parâmetro form_wizard.

action :next, on: :member, only: %w[edit], click: {
post: "/sales/:resource.id",
data: { venda: ":resource" },
params: { form_wizard: 2 },
success: { toast: :success, update: "resource" }
}

🔁 Esse comportamento pode ser usado em múltiplos botões como "Avançar", "Voltar", "Finalizar" etc.


🔒 Validando etapas com Callbacks

Você pode utilizar o valor do form_wizard atual (enviado no params) dentro dos seus callbacks de validação para criar regras condicionais por etapa.

Por exemplo:

before_validation :validate_step3, if: -> { form_wizard.to_i == 3 }

def validate_step3
if value < 0
errors.add(:value, :invalid)
end
end

🔍 O parâmetro form_wizard estará disponível dentro do ciclo de vida da requisição como um método no modelo (form_wizard) e pode ser usado para pular validações desnecessárias em etapas anteriores.


✅ Benefícios do Form Wizard no Nerdify

  • Divisão lógica e visual das etapas de um formulário complexo
  • Navegação fluida controlada por actions
  • Validações dinâmicas com form_wizard.to_i
  • Controle total via backend — sem precisar escrever lógica no frontend
  • Total integração com DSL do Nerdify (fieldsets + actions + callbacks)

💡 Dica

Combine o uso de form_wizard, protected: true, backend_if e render_if para montar formulários altamente dinâmicos, seguros e declarativos, com validações específicas para cada fase.

🧠 Todas as etapas, botões e lógica permanecem centralizadas no modelo e nos controladores, seguindo o padrão do Nerdify de backend descritivo e frontend automático.


Esse bloco pode ser inserido logo após a seção de tabs nos fieldsets para manter a coerência do conteúdo. Se quiser, posso atualizar diretamente sua versão do documento também!

🎨 Estilo visual com styles

O Nerdify aplica por padrão o estilo definido na constante Nerdify::Component::FIELDSET em cada fieldset. Você pode sobrescrever algum dos valores mantendo o restante usando o merge na constante:

fieldset :name, styles: Nerdify::Component::FIELDSET.merge({background_color: :primary}) do
end

Ou substituir completamente definindo o hash diretamente:

fieldset :name, styles: { background_color: :primary } do
end

A estilização padrão de um fieldset é a seguinte:

{
background: 'white',
border: {left: 1, top: 1, right: 1, bottom: 1},
padding: {left: 3, top: 3, bottom: 3, right: 3},
rounded: {top_left: 2, top_right: 2, bottom_left: 2, bottom_right: 2},
wizard_color: 'primary',
wizard_contrast_color: 'white',
color: 'dark',
border_color: 'default',
shadow: 0,
blur: 0,
margin: {
left: 0,
right: 0,
top: 0,
bottom: 2
},
align: {
top: 'left',
left: 'left',
right: 'right',
bottom: 'left',
body: 'left'
},
vertical_align: { default: 'stretch',
top: 'center',
left: 'center',
right: 'center',
bottom: 'center',
body: 'center'
},
orientation: {
top: 'horizontal',
bottom: 'horizontal',
left: 'horizontal',
right: 'horizontal',
body: 'horizontal'
},
font_size: '',
font_weight: ''
}

🔍 Acessando os fieldsets em tempo de execução

Você pode acessar a estrutura definida com:

Customer.nerdify.fieldsets

Isso retorna um array de objetos OpenStruct com todas as opções e filhos definidos. Exemplo:

fieldset = Customer.nerdify.fieldsets.first
puts fieldset.name # => :dados_gerais
puts fieldset.options[:header] # => true
puts fieldset.fieldsets.size # => 2

Os fieldsets são essenciais para estruturar e organizar as telas no Nerdify, mantendo modularidade, clareza visual e controle condicional — tudo de forma declarativa e automatizada. 🚀