Wie erstelle ich in Rails mehrere Senden-Schaltflächen für dasselbe Formular?

96

Ich muss mehrere Senden-Schaltflächen haben.

Ich habe ein Formular, das eine Instanz von Contact_Call erstellt.

Eine Schaltfläche erstellt es wie gewohnt.

Die andere Schaltfläche erstellt es, muss jedoch einen anderen Attributwert als den Standardwert haben und das Attribut für ein anderes, aber verwandtes Modell festlegen, das in der Steuerung verwendet wird.

Wie mache ich das? Ich kann die Route nicht ändern. Gibt es also eine Möglichkeit, eine andere Variable zu senden, die von [: params] erfasst wird?

Und wenn ja, was mache ich dann im Controller, um eine case-Anweisung einzurichten?

Timothy T.
quelle
Mögliches Duplikat von Rails: Multi-Submit-Buttons in einem Formular
Joshua Pinter
3
Dieser ist älter und hat mehr Stimmen. Wenn überhaupt, sollte das oben genannte als Duplikat davon geschlossen werden ...
Taryn East

Antworten:

129

Sie können mehrere Senden-Schaltflächen erstellen und jeweils einen anderen Wert angeben:

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

Dies wird Folgendes ausgeben:

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

In Ihrem Controller wird der Wert der übermittelten Schaltfläche durch den Parameter identifiziert commit. Überprüfen Sie den Wert, um die erforderliche Verarbeitung durchzuführen:

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

Denken Sie jedoch daran, dass dies Ihre Sicht eng mit dem Controller verbindet, was möglicherweise nicht sehr wünschenswert ist.

Anurag
quelle
1
Das ist etwas Neues. Danke @Anurag!
Shripad Krishna
1
Setzen Sie also einfach das 'A' und erstellen Sie automatisch den Parameternamen = 'Festschreiben'?
Timothy T.
Gibt es eine Möglichkeit, wie Sie sagten, die Ansicht nicht fest mit dem Controller zu koppeln? Zum Beispiel, damit die Senden-Schaltflächen die URL ändern? Es scheint, dass dies nicht unbedingt schlecht ist, da ein Formular Variablen sendet, die das Verhalten des Controllers ändern können, auch wenn der Benutzer eingibt, welche Auswahl die Schaltfläche hat.
Timothy T.
1
Sie können ein Formularaktionsattribut nicht ohne einen chaotischen js-Hack ändern.
Ben Orozco
Das spontane Ändern des Formularaktionsattributs ist eine sprödeere Lösung. Die Verwendung des Commit-Attributs ist weniger sinnvoll. Alternativ können Sie die zweite Senden-Schaltfläche in ein anderes Formular einschließen und einen Parameter übergeben, der in dieselbe Aktion geändert werden muss. Es ist jedoch nicht viel anders, als sich auf die Werte der 2 Senden-Schaltflächen zu verlassen. Ohne mehr darüber zu wissen, wie Sie dieses Ding eingerichtet haben, wäre die beste Lösung bisher die mit 2 Senden-Schaltflächen.
Anurag
74

Es gibt auch einen anderen Ansatz, bei dem das Formatierungsattribut auf der Schaltfläche "Senden" verwendet wird:

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

Der Code bleibt sauber, da die standardmäßige Schaltfläche zum Erstellen keine Änderung erfordert. Sie fügen nur einen Routing-Pfad für die spezielle Schaltfläche ein:

formaction:
Der URI eines Programms, das die vom Eingabeelement übermittelten Informationen verarbeitet, wenn es sich um eine Übermittlungsschaltfläche oder ein Bild handelt. Wenn angegeben, wird das Aktionsattribut des Formulareigners des Elements überschrieben . Quelle: MDN

Patpir
quelle
8
Mir ist klar, dass die Frage alt ist, aber ich rate den Lesern, dass diese prägnante Lösung eine bessere Berücksichtigung verdient.
Jerome
2
Ich wünschte, ich hätte diese Antwort gefunden, als ich zum ersten Mal dieselbe Frage hatte. Ich bin froh, dass ich mich entschlossen habe, diesmal etwas tiefer zu schauen. Tolle Lösung.
Rockusbacchus
1
Diese Lösung gefällt mir sehr gut. Ich musste jedoch ein verstecktes Feld mit dem CSRF-Token hinzufügen, obwohl ich bereits Formular-Helfer verwendete oder Rails das Token nicht akzeptierte. Ich konnte keine bessere Problemumgehung finden und bin mir immer noch nicht sicher, warum genau dies passiert, oder wenn ich das Token erneut hinzufüge, wird das Problem behoben.
irruputuncu
Ich denke, dies ist die bessere Lösung, da sie die Prinzipien der Einzelverantwortung respektiert und die Dinge klar hält. Jede Taste führt ihre eigene Aktion aus und hält die Logik in den Controllern einfach.
Khalil Gharbaoui
29

Sie können alternativ erkennen, welche Taste gedrückt wurde, indem Sie den Attributnamen ändern.

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

Es ist ein bisschen unangenehm, weil Sie nach dem Vorhandensein von Parametertasten suchen müssen, anstatt nur den params[:commit]Wert zu überprüfen : Sie erhalten params[:a_button]oder params[:b_button]je nachdem, welcher gedrückt wurde.

Masciugo
quelle
2
Entkoppelt die Sicht immer noch nicht vom Controller.
Slowpoison
1
Ja, wenn Entkopplung bedeutet, eine Logik in der Aktion zu vermeiden, um zur endgültigen Aktion zu gelangen, haben Sie Recht, sind sie dennoch gekoppelt. Ich habe nur gemeint, dass Ihr Controller unabhängig von den Angaben auf der Schaltfläche ist, wenn Sie das Namensattribut in dieser Logik verwenden. Danke, bearbeitet
masciugo
4
Dieser scheint in i18n-Situationen besser zu sein als der akzeptierte, da "Wert" angezeigt wird und wenn Sie Unicode-Zeichen anzeigen, dies unordentlich wird.
Xji
2
Die Parameter werden jedoch nicht durchgelassen. Ich benutze simple_form gem. Gibt es eine Korrelation?
Xji
1
Dadurch wird die Ansicht nicht vom Controller entkoppelt, aber zumindest der vom Controller angezeigte Text. viel besser IMO.
Mic Fok
13

Ähnliche Lösung wie bei @ vss123 ohne Verwendung von Edelsteinen:

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

Beachten Sie, dass ich die Verwendung von Wert vermeide und stattdessen den Eingabenamen verwende, da der Wert der Schaltfläche "Senden" häufig internationalisiert / übersetzt wird. Außerdem würde ich vermeiden, dies zu oft zu verwenden, da es Ihre Routendatei schnell überladen wird.

Tadas Sasnauskas
quelle
9

Wir haben mit erweiterten Einschränkungen in Schienen gelöst .

Die Idee ist, denselben Pfad (und damit dieselbe benannte Route und Aktion) zu haben, jedoch mit Einschränkungen, die an verschiedene Aktionen weitergeleitet werden.

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

CommitParamRoutingist eine einfache Klasse mit einer Methode, matches?die true zurückgibt, wenn der Commit-Parameter mit dem angegebenen Instanzattr übereinstimmt. Wert.

Dies ist als Gem Commit_param_matching verfügbar .

Silikonsenthil
quelle
3

Eine alte Frage, aber da ich mich mit der gleichen Situation befasst habe, dachte ich, ich würde meine Lösung veröffentlichen. Ich verwende Controller-Konstanten, um eine Diskrepanz zwischen der Controller-Logik und der Ansichtsschaltfläche zu vermeiden.

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

Und dann in der Ansicht:

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

Auf diese Weise lebt der Text nur an einer Stelle - als Konstante in der Steuerung. Ich habe jedoch noch nicht versucht herauszufinden, wie ich das machen soll.

Draknor
quelle
Was meinst du mit "i18n"?
skrrgwasme
War dies der Verwendung von Einschränkungen in der Route vorzuziehen? Vielen Dank!
Timothy T.
@Scott: i18n bedeutet "Internationalisierung" - wie würden Sie im Grunde mehrere Sprachen unterstützen? Ich habe mich nicht wirklich damit befasst, daher bin ich nicht sehr vertraut damit, wie es funktioniert oder wie es implementiert wird.
Draknor
@Angela - wahrscheinlich nicht :) Und tatsächlich habe ich nach der Überarbeitung meines Codes einfach mehrere Formulare mit jeweils unterschiedlichen Aktionen erstellt und nicht nur ein einziges monolithisches Formular, das eine Reihe nicht verwandter Formulare enthielt.
Draknor
1

Dank nested_form_fields habe ich eine variable Anzahl von Senden-Schaltflächen in meinem Formular, sodass es mir nicht ausreichte, nur den Namen zu verwenden. Am Ende habe ich ein verstecktes Eingabefeld in das Formular aufgenommen und es mit Javascript ausgefüllt, wenn eine der Schaltflächen zum Senden des Formulars gedrückt wurde.

Shawn Walton
quelle