¿Cómo creo varios botones de envío para el mismo formulario en Rails?

97

Necesito tener varios botones de envío.

Tengo un formulario que crea una instancia de Contact_Call.

Un botón lo crea normalmente.

El otro botón lo crea, pero debe tener un valor de atributo diferente del predeterminado, y también debe establecer el atributo en un modelo diferente pero relacionado que se usa en el controlador.

¿Cómo puedo hacer eso? No puedo cambiar la ruta, así que ¿hay alguna manera de enviar una variable diferente que sea recogida por [: params]?

Y si lo hago, ¿qué hago en el controlador, configurar una declaración de caso?

Timothy T.
fuente
posible duplicado de Rails: botones de envío múltiple en un formulario
Joshua Pinter
3
Este es más antiguo y tiene más votos. Si algo de lo anterior debería cerrarse como un duplicado de esto ...
Taryn East

Respuestas:

129

Puede crear varios botones de envío y proporcionar un valor diferente a cada uno:

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

Esto dará como resultado:

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

Dentro de su controlador, el valor del botón enviado será identificado por el parámetro commit. Verifique el valor para hacer el procesamiento requerido:

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

Sin embargo, recuerde que esto une estrechamente su vista al controlador, lo que puede no ser muy deseable.

Anurag
fuente
1
Eso es algo nuevo. ¡Gracias @Anurag!
Shripad Krishna
1
así que simplemente poniendo la 'A' creará automáticamente el nombre del parámetro = 'confirmar'?
Timothy T.
¿Hay alguna manera, como dijiste, de no acoplar estrechamente la vista al controlador? por ejemplo, para que los botones de envío cambien la URL? Se parece que esto no es necesariamente malo, porque un formulario envía las variables que pueden cambiar el comportamiento de controlador de HTE, I ncluso si la entrada del usuario, que la selección del botón es?
Timothy T.
1
No puede cambiar un atributo de acción de formulario sin un truco js desordenado.
Ben Orozco
Cambiar el atributo de acción de formulario sobre la marcha es una solución más frágil. El uso del atributo de confirmación lo es menos. Como alternativa, podría envolver el segundo botón de envío dentro de un formulario diferente y pasar un parámetro que debe cambiarse a la misma acción. Pero no es muy diferente a confiar en los valores de los 2 botones de envío. Sin saber más sobre cómo ha configurado esto, la mejor solución hasta ahora sería la con 2 botones de envío.
Anurag
74

También hay otro enfoque, utilizando el atributo de formación en el botón de envío:

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

El código se mantiene limpio, ya que el botón de creación estándar no necesita ningún cambio, solo inserta una ruta de enrutamiento para el botón especial:

formaction:
La URI de un programa que procesa la información enviada por el elemento de entrada, si es un botón de envío o una imagen. Si se especifica, anula el atributo de acción del propietario del formulario del elemento . Fuente: MDN

patpir
fuente
2
esto es compatible con todos los navegadores w3schools.com/tags/att_button_formaction.asp w3schools.com/tags/att_input_formaction.asp
Sumit Garg
8
Me doy cuenta de que la pregunta es antigua, pero advierto a los lectores que esta solución concisa merece una mejor consideración.
Jerome
2
Ojalá hubiera encontrado esta respuesta la primera vez que tuve la misma pregunta. Me alegro de haber decidido profundizar un poco más esta vez. Gran solucion
rockusbacchus
1
Me gusta mucho esta solución. Sin embargo, tuve que agregar un campo oculto con el token CSRF aunque ya estaba usando ayudantes de formulario o Rails no aceptaba el token. No pude encontrar una mejor solución y todavía no estoy seguro de por qué sucede exactamente esto o simplemente agregar el token nuevamente lo soluciona.
irruputuncu
Creo que esta es la mejor solución porque respeta los principios de responsabilidad única y mantiene las cosas claras, cada botón realiza su propia acción manteniendo la lógica en los controladores simple.
Khalil Gharbaoui
29

Alternativamente, puede reconocer qué botón se presionó cambiando su nombre de atributo.

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

Es un poco incómodo porque debe verificar la presencia de las teclas params en lugar de simplemente verificar el params[:commit]valor: recibirá params[:a_button]o params[:b_button]dependiendo de cuál se presionó.

masciugo
fuente
2
Aún no desacopla la vista del controlador.
veneno lento
1
Sí, si desacoplar significa evitar cierta lógica en la acción para encaminarnos a la acción final, tienes razón, todavía están acoplados. Solo quise decir que si usa el atributo de nombre en esa lógica, su controlador es independiente de lo que se muestra en el botón. Gracias, editado
masciugo
4
Este parece ser mejor que el aceptado en situaciones de i18n porque se muestra "valor" y si está mostrando caracteres Unicode se volvería complicado.
xji
2
Sin embargo, los parámetros no se dejan pasar. Estoy usando la gema simple_form. ¿Existe alguna correlación?
xji
1
Esto no desacopla la vista del controlador, pero al menos desacopla el texto que se muestra del controlador. mucho mejor en mi opinión.
Mic Fok
13

Solución similar a la sugerida por @ vss123 sin usar gemas:

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

Tenga en cuenta que evito usar valor y uso el nombre de entrada en su lugar, ya que el valor del botón enviar a menudo se internacionaliza / traduce. Además, evitaría usar esto demasiado ya que saturará rápidamente su archivo de rutas.

Tadas Sasnauskas
fuente
9

Resolvimos usando restricciones avanzadas en rieles.

La idea es tener la misma ruta (y por lo tanto la misma ruta y acción nombradas) pero con restricciones de enrutamiento a diferentes acciones.

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

CommitParamRoutinges una clase simple que tiene un método matches?que devuelve verdadero si el parámetro de confirmación coincide con el atributo de instancia dado. valor.

Esto está disponible como una gema commit_param_matching .

siliconsenthil
fuente
3

Una vieja pregunta, pero como he estado lidiando con la misma situación, pensé en publicar mi solución. Estoy usando constantes de controlador para evitar introducir una discrepancia entre la lógica del controlador y el botón de visualización.

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

Y luego en la vista:

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

De esta manera, el texto solo vive en un lugar, como una constante en el controlador. Sin embargo, todavía no he intentado averiguar cómo solucionarlo.

Draknor
fuente
¿Qué quieres decir con "i18n"?
skrrgwasme
¿Era preferible usar restricciones en la ruta? ¡Gracias!
Timothy
@Scott: i18n significa 'internacionalización'; básicamente, ¿cómo admitirías varios idiomas? Realmente no lo he investigado, por lo que no estoy muy familiarizado con cómo funciona o cómo implementarlo.
Draknor
@Angela - probablemente no :) Y de hecho, después de refactorizar mi código, simplemente creé múltiples formularios, cada uno con diferentes acciones, en lugar de un solo formulario monolítico que contenía un montón de formularios no relacionados.
Draknor
1

Tengo un número variable de botones de envío en mi formulario gracias a nested_form_fields, por lo que usar el nombre no fue suficiente para mí. Terminé incluyendo un campo de entrada oculto en el formulario y usando Javascript para completarlo cuando se presionó uno de los botones de envío del formulario.

Shawn Walton
fuente