如何在Rails中为同一表单创建多个提交按钮?

113

我需要有多个提交按钮。

我有一个表单,它创建了 Contact_Call 的一个实例。

一个按钮按照正常方式创建该实例。

另一个按钮创建它,但需要具有不同的 :attribute 值,它还需要在控制器中使用的另一个相关模型上设置属性。

如何做到这一点?我不能更改路由,那么是否有一种方法可以发送不同的变量,然后被[:params]捕获?

如果我这样做,那么在控制器中我该怎么办,设置一个 case 语句吗?


可能是重复问题:Rails:一个表单中有多个提交按钮 - Joshua Pinter
4
这个更老,投票数也更多。如果有什么问题,上面的应该作为它的重复内容而关闭... - Taryn East
7个回答

136

您可以创建多个提交按钮,并为每个按钮提供不同的值:

<% form_for(something) do |f| %>
    ..
    <%= f.submit 'A' %>
    <%= f.submit 'B' %>
    ..
<% end %>
这将输出:
<input type="submit" value="A" id=".." name="commit" />
<input type="submit" value="B" id=".." name="commit" />

在您的控制器内,提交按钮的值将由参数commit识别。检查该值以执行所需的处理:

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

然而,请记住,这会严密地将您的视图与控制器耦合在一起,这可能并不是非常理想的。


1
现在有了新的东西。谢谢@Anurag! - Shripad Krishna
1
那么只是将'A'放置,会自动生成参数名称='commit'吗? - Satchel
1
你不能在没有混乱的js hack的情况下更改表单操作属性。 - Ben Orozco
也许有人可以帮我解决这个问题 http://stackoverflow.com/questions/18476292/rails-two-submit-buttons-but-only-one-remotetrue - John Smith
国际化问题怎么处理? - carpamon
显示剩余6条评论

100

还有另一种方法,使用submit按钮上的formaction属性:

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

标准的创建按钮不需要任何更改,代码保持清洁,你只需为特殊按钮插入一个路由路径:

formaction:
如果是提交按钮或图像,则该输入元素提交的信息将由指定的程序处理的 URI。如果指定了 formaction 属性,则覆盖元素的表单所有者的 action 属性。 来源:MDN


4
这在所有浏览器中都得到支持。http://www.w3schools.com/tags/att_button_formaction.asp http://www.w3schools.com/tags/att_input_formaction.asp - Sumit Garg
10
我意识到这个问题很久了,但我建议读者认真考虑这个简明的解决方案。 - Jerome
2
我希望第一次有这个问题时就能找到这个答案。很高兴这次我决定深入了解一下。非常好的解决方案。 - rockusbacchus
2
我真的很喜欢这个解决方案。然而,尽管我已经使用了表单助手,Rails仍然不接受令牌,因此我不得不添加一个带有CSRF令牌的隐藏字段。我找不到更好的解决方法,也不确定为什么会出现这种情况,或者只是再次添加令牌就可以解决问题。 - irruputuncu
1
@B-M 我找不到当时我写的确切代码了,但我想我使用了这种方法:https://dev59.com/Hmoy5IYBdhLWcg3wguWS - 就像这样添加 <%= hidden_field_tag :authenticity_token, form_authenticity_token %> - irruputuncu
显示剩余2条评论

33

您可以通过更改按钮的属性名称来识别哪个按钮被按下。

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

这有点不舒服,因为你必须检查参数键是否存在,而不是仅仅检查params[:commit]值:你将收到params[:a_button]params[:b_button],具体取决于哪个按钮被按下。


2
仍未将视图与控制器解耦。 - slowpoison
1
是的,如果解耦意味着避免在动作中使用一些逻辑以便路由到最终操作,那么您是正确的,它们仍然是耦合的。我只是想说,如果您在该逻辑中使用name属性,则控制器与按钮上显示的内容无关。谢谢,已编辑。 - masciugo
4
在 i18n 的情况下,这个看起来比被接受的更好,因为它会显示“value”,如果你显示 Unicode 字符,那么它会变得混乱。 - xji
2
然而参数没有被传递。我正在使用simple_form gem。有任何关联吗? - xji
1
这并没有将视图与控制器解耦,但至少它将显示的文本与控制器解耦了。在我看来更好。 - Mic Fok
1
类似于向基的问题。我正在使用form_tag,但参数没有传递过来。 - ryan2johnson9

13

与@vss123提出的建议类似,但不使用任何宝石库的解决方案:

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

请注意,我避免使用value并使用input name,因为提交按钮的值经常需要国际化/翻译。此外,我会避免过多地使用它,因为它会很快使您的路由文件混乱。


10

我们使用Rails中的高级约束来解决问题。

这个想法是使用相同的路径(因此具有相同的命名路由和操作),但是通过约束条件将其路由到不同的操作。

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

CommitParamRouting 是一个简单的类,具有一个方法 matches?,如果提交参数与给定的实例属性值匹配,则返回 true。

这作为 gem 可在 commit_param_routing 中找到。


3

这是一个老问题,但由于我也遇到了同样的情况,所以我想发布我的解决方案。我使用控制器常量来避免引入控制器逻辑和视图按钮之间的差异。

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

然后在视图中:

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

这样,文本只存在于控制器中的一个常量中。我还没有尝试想出如何进行国际化处理。


"i18n" 是什么意思? - skrrgwasme
这种方法是否比在路由中使用约束更可取?谢谢! - Satchel
@Scott:i18n 意味着“国际化”,基本上是指如何支持多种语言。我还没有真正研究过它,所以对它的工作原理或实现方式不是很熟悉。 - Draknor
@Angela - 可能不太可能 :) 事实上,在重构代码后,我只是创建了多个表单,每个表单具有不同的操作,而不是包含一堆不相关表单的单个庞大表单。 - Draknor

1

由于使用了nested_form_fields,我的表单上有可变数量的提交按钮,因此仅使用名称是不够的。最终我在表单中包含了一个隐藏的输入字段,并在表单提交按钮之一被按下时使用JavaScript来填充它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接