Comment créer plusieurs boutons d'envoi pour le même formulaire dans Rails?

97

J'ai besoin de plusieurs boutons d'envoi.

J'ai un formulaire qui crée une instance de Contact_Call.

Un bouton le crée normalement.

L'autre bouton le crée mais doit avoir une valeur d'attribut différente de la valeur par défaut, et il doit également définir l'attribut sur un modèle différent, mais lié, utilisé dans le contrôleur.

Comment je fais ça? Je ne peux pas changer l'itinéraire, alors existe-t-il un moyen d'envoyer une variable différente qui est captée par [: params]?

Et si je fais alors, que dois-je faire dans le contrôleur, mettre en place une déclaration de cas?

Timothy T.
la source
3
Celui-ci est plus ancien et a plus de voix. Si quelque chose de ce qui précède doit être fermé comme un double de ce ...
Taryn East

Réponses:

129

Vous pouvez créer plusieurs boutons d'envoi et fournir une valeur différente à chacun:

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

Cela produira:

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

À l'intérieur de votre contrôleur, la valeur du bouton soumis sera identifiée par le paramètre commit. Vérifiez la valeur pour effectuer le traitement requis:

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

Cependant, rappelez-vous que cela couple étroitement votre vue au contrôleur, ce qui peut ne pas être très souhaitable.

Anurag
la source
1
Maintenant c'est quelque chose de nouveau. Merci @Anurag!
Shripad Krishna
1
il suffit donc de mettre le 'A' pour créer automatiquement le paramètre name = 'commit'?
Timothy T.
y a-t-il un moyen comme vous l'avez dit de ne pas coupler étroitement la vue au contrôleur? par exemple, pour les boutons de soumission pour changer l'URL? Il semble que ce ne soit pas forcément mauvais car un formulaire soumet des variables qui peuvent changer le comportement du contrôleur, même si c'est une entrée utilisateur, quelle est la sélection du bouton?
Timothy T.
1
Vous ne pouvez pas modifier un attribut d'action de formulaire sans un hack js désordonné.
Ben Orozco
Changer l'attribut d'action de formulaire à la volée est une solution plus fragile. L'utilisation de l'attribut commit l'est moins. Vous pouvez également envelopper le deuxième bouton d'envoi dans un formulaire différent et transmettre un paramètre qui doit être modifié pour la même action. Mais ce n'est pas très différent de se fier aux valeurs des 2 boutons de soumission. Sans savoir plus comment vous avez configuré cette chose, la meilleure solution à ce jour serait la avec 2 boutons de soumission.
Anurag le
74

Il existe également une autre approche, utilisant l'attribut formaction sur le bouton d'envoi:

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

Le code reste propre, car le bouton de création standard ne nécessite aucune modification, vous insérez uniquement un chemin de routage pour le bouton spécial:

formaction:
L'URI d'un programme qui traite les informations soumises par l'élément d'entrée, s'il s'agit d'un bouton ou d'une image d' envoi . S'il est spécifié, il remplace l'attribut action du propriétaire du formulaire de l'élément . Source: MDN

patpir
la source
2
ceci est pris en charge sur tous les navigateurs w3schools.com/tags/att_button_formaction.asp w3schools.com/tags/att_input_formaction.asp
Sumit Garg
8
Je me rends compte que la question est ancienne, mais j'informe les lecteurs que cette solution concise mérite une meilleure considération.
Jerome
2
J'aurais aimé trouver cette réponse la première fois que j'avais cette même question. Je suis content d'avoir décidé de regarder un peu plus profondément cette fois. Excellente solution.
rockusbacchus
1
J'aime vraiment cette solution. Cependant, j'ai dû ajouter un champ caché avec le jeton CSRF même si j'utilisais déjà des aides de formulaire ou que Rails n'accepterait pas le jeton. Je n'ai pas pu trouver une meilleure solution de contournement et je ne sais toujours pas pourquoi exactement cela se produit ou simplement ajouter le jeton à nouveau le corrige.
irruputuncu
Je pense que c'est la meilleure solution car elle respecte les principes de responsabilité unique et maintient les choses claires, chaque bouton effectue sa propre action en gardant la logique des contrôleurs simple.
Khalil Gharbaoui le
29

Vous pouvez également reconnaître quel bouton a été enfoncé en changeant son nom d'attribut.

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

C'est un peu inconfortable car vous devez vérifier la présence des touches de paramètres au lieu de simplement vérifier la params[:commit]valeur: vous recevrez params[:a_button]ou en params[:b_button]fonction de celle qui a été pressée.

masciugo
la source
2
Ne dissocie toujours pas la vue du contrôleur.
slowpoison
1
Oui, si découpler signifie éviter une certaine logique dans l'action afin de diriger vers l'action finale vous avez raison, ils sont toujours couplés. Je voulais simplement dire que si vous utilisez l'attribut name dans cette logique, votre contrôleur est indépendant de ce qui est affiché sur le bouton. Merci, édité
masciugo
4
Celui-ci semble être meilleur que celui accepté dans les situations i18n car «valeur» est affiché et si vous affichez des caractères Unicode, cela deviendrait désordonné.
xji
2
Cependant, les paramètres ne sont pas révélés. J'utilise la gemme simple_form. Y a-t-il une corrélation.
xji
1
Cela ne dissocie pas la vue du contrôleur, mais au moins, il dissocie le texte affiché du contrôleur. beaucoup mieux IMO.
Mic Fok
13

Solution similaire à celle suggérée par @ vss123 sans utiliser de gemmes:

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

Notez que j'évite d'utiliser la valeur et d'utiliser le nom d'entrée à la place, car la valeur du bouton d'envoi est souvent internationalisée / traduite. De plus, j'éviterais de trop l'utiliser car cela encombrera rapidement votre fichier de routes.

Tadas Sasnauskas
la source
9

Nous avons résolu en utilisant des contraintes avancées dans les rails.

L'idée est d'avoir le même chemin (et donc la même route & action nommée) mais avec des contraintes de routage vers différentes actions.

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

CommitParamRouting est une classe simple qui a une méthode matches? qui renvoie true si le paramètre de validation correspond à l'instance attr. valeur.

Ceci est disponible sous forme de gem commit_param_matching .

siliconsenthil
la source
3

Une vieille question, mais comme je fais face à la même situation, j'ai pensé afficher ma solution. J'utilise des constantes de contrôleur pour éviter d'introduire une différence entre la logique du contrôleur et le bouton d'affichage.

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

Et puis dans la vue:

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

De cette façon, le texte ne vit qu'à un seul endroit - en tant que constante dans le contrôleur. Cependant, je n'ai pas encore essayé de comprendre comment y parvenir.

Draknor
la source
Qu'entendez-vous par «i18n»?
skrrgwasme
Était-ce préférable à l'utilisation de contraintes sur l'itinéraire? Merci!
Timothy T.
@Scott: i18n signifie `` internationalisation '' - en gros, comment supporteriez-vous plusieurs langues. Je ne l'ai pas vraiment examiné, donc je ne suis pas très familier avec son fonctionnement ou sa mise en œuvre.
Draknor
@Angela - probablement pas :) Et en fait, après avoir refactoré mon code, j'ai simplement créé plusieurs formulaires, chacun avec des actions différentes, plutôt qu'une seule forme monolithique contenant un tas de formulaires sans rapport.
Draknor
1

J'ai un nombre variable de boutons d'envoi sur mon formulaire grâce à nested_form_fields, donc utiliser le nom ne me suffisait pas. J'ai fini par inclure un champ de saisie caché dans le formulaire et utiliser Javascript pour le remplir lorsque l'un des boutons de soumission du formulaire était enfoncé.

Shawn Walton
la source