Jak utworzyć wiele przycisków przesyłania dla tego samego formularza w Railsach?

98

Potrzebuję wielu przycisków przesyłania.

Mam formularz, który tworzy wystąpienie Contact_Call.

Jeden przycisk tworzy to normalnie.

Drugi przycisk tworzy go, ale musi mieć inną wartość atrybutu od domyślnej, a także musi ustawić atrybut na inny, ale powiązany model używany w kontrolerze.

W jaki sposób mogę to zrobić? Nie mogę zmienić trasy, więc czy istnieje sposób na wysłanie innej zmiennej, która jest pobierana przez [: params]?

A jeśli to zrobię, co mam zrobić w kontrolerze, założyć zestawienie przypadków?

Timothy T.
źródło
3
Ten jest starszy i ma więcej głosów. Jeśli cokolwiek, powyższe powinno zostać zamknięte jako duplikat tego ...
Taryn East

Odpowiedzi:

129

Możesz utworzyć wiele przycisków przesyłania i nadać każdemu inną wartość:

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

Spowoduje to wyświetlenie:

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

Wewnątrz Twojego kontrolera wartość przesłanego przycisku będzie identyfikowana przez parametr commit. Sprawdź wartość, aby wykonać wymagane przetwarzanie:

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

Pamiętaj jednak, że to ściśle łączy twój pogląd z kontrolerem, co może nie być zbyt pożądane.

Anurag
źródło
1
To jest coś nowego. Dzięki @Anurag!
Shripad Krishna
1
więc po prostu wstawienie „A” powoduje automatyczne utworzenie nazwy parametru = „commit”?
Timothy T.
czy jest sposób, jak powiedziałeś, aby nie wiązać mocno widoku z kontrolerem? na przykład, aby przyciski przesyłania zmieniły adres URL? To wydaje się , że to niekoniecznie jest złe, ponieważ forma utrzymuje zmienne, które mogą zmienić zachowanie kontrolera hte, e ven jeśli dane wejściowe użytkownika, którego wybór przycisku jest?
Timothy T.
1
Nie możesz zmienić atrybutu akcji formularza bez niechlujnego hackowania js.
Ben Orozco,
Zmiana atrybutu akcji formularza w locie to bardziej kruche rozwiązanie. Używanie atrybutu commita jest mniej prawdopodobne. Alternatywnie możesz umieścić drugi przycisk przesyłania w innym formularzu i przekazać parametr, który należy zmienić na tę samą akcję. Ale nie różni się to zbytnio od polegania na wartościach 2 przycisków przesyłania. Nie wiedząc więcej, jak to skonfigurowałeś, najlepszym jak dotąd rozwiązaniem byłoby użycie 2 przycisków przesyłania.
Anurag,
75

Istnieje również inne podejście, wykorzystujące atrybut formaction na przycisku przesyłania:

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

Kod pozostaje czysty, ponieważ standardowy przycisk tworzenia nie wymaga żadnych zmian, wystarczy wstawić ścieżkę routingu dla przycisku specjalnego:

formaction:
identyfikator URI programu, który przetwarza informacje przesłane przez element wejściowy, jeśli jest to przycisk przesyłania lub obraz. Jeśli jest określony, przesłania atrybut akcji właściciela formularza elementu . Źródło: MDN

patpir
źródło
2
jest to obsługiwane we wszystkich przeglądarkach w3schools.com/tags/att_button_formaction.asp w3schools.com/tags/att_input_formaction.asp
Sumit Garg
8
Zdaję sobie sprawę, że to stare pytanie, ale radzę czytelnikom, że to zwięzłe rozwiązanie zasługuje na dokładniejsze rozważenie.
Jerome,
2
Żałuję, że nie znalazłem tej odpowiedzi, gdy po raz pierwszy miałem to samo pytanie. Cieszę się, że tym razem postanowiłem spojrzeć nieco głębiej. Świetne rozwiązanie.
rockusbacchus
1
Bardzo podoba mi się to rozwiązanie. Musiałem jednak dodać ukryte pole z tokenem CSRF, mimo że korzystałem już z pomocników formularzy lub Railsy nie akceptowały tokena. Nie mogłem znaleźć lepszego obejścia i nadal nie jestem pewien, dlaczego dokładnie tak się dzieje, lub ponowne dodanie tokena rozwiązuje problem.
irruputuncu
Myślę, że jest to lepsze rozwiązanie, ponieważ szanuje zasady pojedynczej odpowiedzialności i zapewnia jasność, każdy przycisk wykonuje własną akcję, zachowując prostą logikę w kontrolerach.
Khalil Gharbaoui
29

Alternatywnie możesz rozpoznać, który przycisk został naciśnięty, zmieniając jego nazwę atrybutu.

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

Jest to trochę niewygodne, ponieważ musisz sprawdzić obecność klawiszy parametrów zamiast po prostu sprawdzić params[:commit]wartość: otrzymasz params[:a_button]lub w params[:b_button]zależności od tego, który z nich został naciśnięty.

masciugo
źródło
2
Nadal nie oddziela widoku od kontrolera.
slowpoison
1
Tak, jeśli oddzielenie oznacza unikanie jakiejś logiki w działaniu, aby przejść do ostatecznego działania, masz rację, nadal są one połączone. Chodziło mi tylko o to, że jeśli używasz atrybutu name w tej logice, twój kontroler jest niezależny od tego, co jest pokazane na przycisku. Dzięki, zredagowano
masciugo
4
Ten wydaje się być lepszy niż akceptowany w sytuacjach i18n, ponieważ wyświetla się „wartość”, a jeśli wyświetlasz znaki Unicode, mogłoby się to pogmatwać.
xji
2
Jednak parametry nie są przepuszczane. Używam gem simple_form. Czy jest jakaś korelacja.
xji
1
Nie oddziela to widoku od kontrolera, ale przynajmniej oddziela tekst wyświetlany od kontrolera. znacznie lepsze IMO.
Mic Fok
13

Rozwiązanie podobne do sugerowanego przez @ vss123 bez użycia klejnotów:

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

Zauważ, że unikam używania wartości i zamiast tego używam nazwy wejściowej, ponieważ wartość przycisku przesyłania jest często umiędzynarodowiona / przetłumaczona. Unikałbym też używania tego zbyt często, ponieważ szybko zaśmieci to plik tras.

Tadas Sasnauskas
źródło
9

Rozwiązaliśmy za pomocą zaawansowanych ograniczeń w szynach.

Chodzi o to, aby mieć tę samą ścieżkę (a zatem tę samą nazwaną trasę i akcję), ale z ograniczeniami kierującymi do różnych akcji.

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

CommitParamRoutingto prosta klasa, która ma metodę, matches?która zwraca wartość true, jeśli parametr zatwierdzenia jest zgodny z atrybutem danej instancji. wartość.

Jest to dostępne jako gem commit_param_matching .

siliconsentil
źródło
3

Stare pytanie, ale ponieważ miałem do czynienia z tą samą sytuacją, pomyślałem, że opublikuję swoje rozwiązanie. Używam stałych kontrolera, aby uniknąć rozbieżności między logiką kontrolera a przyciskiem widoku.

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

A potem w widoku:

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

W ten sposób tekst żyje tylko w jednym miejscu - jako stała w kontrolerze. Jednak nie próbowałem jeszcze wymyślić, jak to zrobić.

Draknor
źródło
Co masz na myśli mówiąc „i18n”?
skrrgwasme
Czy było to lepsze od stosowania ograniczeń na trasie? Dzięki!
Timothy T.
@Scott: i18n oznacza „internacjonalizację” - w skrócie, jak byś obsługiwał wiele języków. Tak naprawdę nie przyjrzałem się temu, więc nie jestem zbyt zaznajomiony z tym, jak to działa ani jak go wdrożyć.
Draknor
@Angela - prawdopodobnie nie :) I tak naprawdę, po refaktoryzacji mojego kodu po prostu stworzyłem wiele formularzy, każda z innymi akcjami, zamiast pojedynczej monolitycznej formy zawierającej kilka niepowiązanych ze sobą form.
Draknor
1

Mam zmienną liczbę przycisków przesyłania w moim formularzu dzięki nested_form_fields, więc samo użycie nazwy nie było dla mnie wystarczające. Skończyło się na tym, że włączyłem ukryte pole wejściowe do formularza i użyłem JavaScript do wypełnienia go, gdy naciśnięto jeden z przycisków przesyłania formularza.

Shawn Walton
źródło