Como faço para criar vários botões de envio para o mesmo formulário no Rails?


97

Eu preciso ter vários botões de envio.

Eu tenho um formulário que cria uma instância de Contact_Call.

Um botão o cria normalmente.

O outro botão o cria, mas precisa ter um valor: attribute diferente do padrão e também precisa definir o atributo em um modelo diferente, mas relacionado, usado no controlador.

Como faço isso? Não posso mudar a rota, então há uma maneira de enviar uma variável diferente que é selecionada por [: params]?

E se eu fizer isso, o que eu faço no controlador, configurar uma declaração de caso?



3
Este é mais antigo e tem mais votos. Se qualquer coisa acima deve ser fechada como uma duplicata deste ...
— Taryn East

Respostas:


129

Você pode criar vários botões de envio e fornecer um valor diferente para cada um:

<% form_for(something) do |f| %>
    ..
    <%= f.submit 'A' %>
    <%= f.submit 'B' %>
    ..
<% end %>

Isso resultará em:

<input type="submit" value="A" id=".." name="commit" />
<input type="submit" value="B" id=".." name="commit" />

Dentro do seu controlador, o valor do botão enviado será identificado pelo parâmetro commit. Verifique o valor para fazer o processamento necessário:

def <controller action>
    if params[:commit] == 'A'
        # A was pressed 
    elsif params[:commit] == 'B'
        # B was pressed
    end
end

No entanto, lembre-se de que isso une fortemente sua visão ao controlador, o que pode não ser muito desejável.


1
Agora isso é algo novo. Obrigado @Anurag!
— Shripad Krishna

1
então, basta colocar o 'A' para criar automaticamente o nome do parâmetro = 'commit'?
— Timothy T.

há uma maneira, como você disse, de não acoplar firmemente a visualização ao controlador? por exemplo, para os botões de envio para alterar o URL? Ele parece que isso não é necessariamente ruim, porque um formulário envia variáveis que podem mudar o comportamento do controlador de hte, e ven se a entrada do usuário, que a seleção do botão é?
— Timothy T.

1
Você não pode alterar um atributo de ação de formulário sem um hack bagunçado do js.
— Ben Orozco

Alterar o atributo de ação do formulário instantaneamente é uma solução mais frágil. Usar o atributo commit é menos. Você poderia, como alternativa, envolver o segundo botão de envio em um formulário diferente e passar um parâmetro que precisa ser alterado para a mesma ação. Mas não é muito diferente de confiar nos valores dos 2 botões de envio. Sem saber mais como você configurou isso, a melhor solução até agora seria com 2 botões de envio.
— Anurag de

74

Também há outra abordagem, usando o atributo formaction no botão de envio:

<% form_for(something) do |f| %>
    ...
    <%= f.submit "Create" %>
    <%= f.submit "Special Action", formaction: special_action_path %>
<% end %>

O código permanece limpo, já que o botão de criação padrão não precisa de nenhuma alteração, você apenas insere um caminho de roteamento para o botão especial:

formação:
O URI de um programa que processa as informações enviadas pelo elemento de entrada, se for um botão de envio ou imagem. Se especificado, ele substitui o atributo de ação do proprietário do formulário do elemento . Fonte: MDN



8
Sei que a pergunta é antiga, mas aconselho os leitores que essa solução concisa merece uma consideração melhor.
— Jerome

2
Eu gostaria de ter encontrado essa resposta na primeira vez que fiz a mesma pergunta. Estou feliz por ter decidido olhar um pouco mais a fundo desta vez. Ótima solução.
— rockusbacchus

1
Eu realmente gosto dessa solução. Porém, eu tive que adicionar um campo oculto com o token CSRF mesmo que eu já estivesse usando auxiliares de formulário ou o Rails não aceitaria o token. Não consegui encontrar uma solução alternativa melhor e ainda não tenho certeza de por que exatamente isso acontece ou apenas adicionar o token novamente corrige o problema.
— irruputuncu

Acho que esta é a melhor solução porque respeita os princípios de responsabilidade única e mantém as coisas claras, cada botão executa sua própria ação mantendo a lógica nos controladores simples.
— Khalil Gharbaoui

29

Você pode, alternativamente, reconhecer qual botão foi pressionado alterando seu nome de atributo.

<% form_for(something) do |f| %>
    ..
    <%= f.submit 'A', name: 'a_button' %>
    <%= f.submit 'B', name: 'b_button' %>
    ..
<% end %>

É um pouco desconfortável porque você tem que verificar a presença das teclas de parâmetro ao invés de simplesmente verificar o params[:commit]valor: você receberá params[:a_button]ou params[:b_button]dependendo de qual foi pressionada.


2
Ainda não desacopla a visão do controlador.
— slowpoison

1
Sim, se desacoplar significa evitar alguma lógica na ação a fim de direcionar para a ação final que você está certo, eles ainda estão acoplados. Eu apenas quis dizer que se você usar o atributo name nessa lógica, seu controlador é independente do que é mostrado no botão. Obrigado, editado
— masciugo

4
Este parece ser melhor do que o aceito em situações i18n porque "valor" é exibido e se você estiver exibindo caracteres Unicode, ficaria confuso.
— xji

2
No entanto, os parâmetros não estão sendo permitidos. Estou usando a gem simple_form. Existe alguma correlação.
— xji

1
Isso não desacopla a visualização do controlador, mas pelo menos separa o texto exibido do controlador. muito melhor IMO.
— Mic Fok

13

Solução semelhante a uma sugerida por @ vss123 sem usar nenhuma joia:

resources :plan do
  post :save, constraints: lambda {|req| req.params.key?(:propose)}, action: :propose
  post :save, constraints: lambda {|req| req.params.key?(:finalize)}, action: :finalize
end

Observe que eu evito usar valor e uso o nome de entrada em vez disso, pois o valor do botão de envio é frequentemente internacionalizado / traduzido. Além disso, eu evitaria usar muito isso, pois isso bagunçaria rapidamente seu arquivo de rotas.


9

Resolvemos usando restrições avançadas em trilhos.

A ideia é ter o mesmo caminho (e, portanto, a mesma rota e ação nomeadas), mas com restrições de roteamento para ações diferentes.

resources :plan do
  post :save, constraints: CommitParamRouting.new("Propose"), action: :propose
  post :save, constraints: CommitParamRouting.new("Finalize"), action: :finalize
end

CommitParamRoutingé uma classe simples que possui um método matches?que retorna verdadeiro se o parâmetro commit corresponder ao atributo de instância fornecido. valor.

Está disponível como uma gema commit_param_matching .


3

Uma pergunta antiga, mas como estou lidando com a mesma situação, pensei em postar minha solução. Estou usando constantes do controlador para evitar a introdução de uma discrepância entre a lógica do controlador e o botão de visualização.

class SearchController < ApplicationController
  SEARCH_TYPES = {
    :searchABC => "Search ABCs",
    :search123 => "Search 123s"
  }

  def search
    [...]
    if params[:commit] == SEARCH_TYPES[:searchABC]
      [...]
    elsif params[:commit] == SEARCH_TYPES[:search123]
      [...]
    else
      flash[:error] = "Search type not found!"]
      [...]
    end
  end
  [...]          
end

E então na vista:

<% form_for(something) do |f| %>
    [...]
    <%= f.submit SearchController::SEARCH_TYPES[:searchABC] %>
    <%= f.submit SearchController::SEARCH_TYPES[:search123] %>
    [...]
<% end %>

Dessa forma, o texto vive apenas em um lugar - como uma constante no controlador. Ainda não tentei descobrir como resolver isso ainda.


O que você quer dizer com "i18n"?
— skrrgwasme

Isso era preferível a usar restrições na rota? Obrigado!
— Timothy T.

@Scott: i18n significa 'internacionalização' - basicamente, como você suportaria vários idiomas. Eu realmente não olhei para isso, então não estou muito familiarizado com como funciona ou como implementá-lo.
— Draknor de

@Angela - provavelmente não :) E, na verdade, depois de refatorar meu código, simplesmente criei vários formulários, cada um com ações diferentes, em vez de um único formulário monolítico que continha um monte de formulários não relacionados.
— Draknor de

1

Eu tenho um número variável de botões de envio em meu formulário graças a nested_form_fields, então apenas usar o nome não foi suficiente para mim. Acabei incluindo um campo de entrada oculto no formulário e usando Javascript para preenchê-lo quando um dos botões de envio do formulário foi pressionado.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.