【问题标题】:How do I create multiple submit buttons for the same form in Rails?如何在 Rails 中为同一个表单创建多个提交按钮?
【发布时间】:2010-06-12 01:38:24
【问题描述】:

我需要有多个提交按钮。

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

一个按钮照常创建它。

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

我该怎么做?我无法更改路线,所以有没有办法发送由 [:params] 拾取的不同变量?

如果我这样做了,我应该在控制器中做什么,设置一个 case 语句?

【问题讨论】:

标签: ruby-on-rails ruby form-submit


【解决方案1】:

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

<% 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

但是,请记住,这会将您的视图与控制器紧密耦合,这可能不是很理想。

【讨论】:

  • 现在这是新的东西。谢谢@Anurag!
  • 所以只是把'A'自动创建参数name='commit'?
  • 有没有一种方法可以不将视图与控制器紧密耦合?例如,为提交按钮更改 URL? 似乎这不一定是坏事,因为表单提交的变量可以改变 hte 控制器的行为,即使它是用户输入,按钮的选择是什么?
  • 如果没有凌乱的 js hack,您将无法更改表单操作属性。
  • 动态更改表单操作属性是一个更脆弱的解决方案。使用 commit 属性就更少了。作为替代方案,您可以将第二个提交按钮包装在不同的表单中,并传递需要更改为相同操作的参数。但这与依赖 2 个提交按钮的值没有太大区别。在不知道你是如何设置这个东西的情况下,迄今为止最好的解决方案是使用 2 个提交按钮。
【解决方案2】:

还有另一种方法,在提交按钮上使用formaction属性:

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

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

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

【讨论】:

  • 我意识到这个问题已经过时了,但我建议读者,这个简洁的解决方案值得更好地考虑。
  • 我希望我第一次遇到同样的问题时就找到了这个答案。我很高兴这次我决定深入一点。很好的解决方案。
  • 我真的很喜欢这个解决方案。但是,即使我已经在使用表单助手或 Rails 不会接受该令牌,我也必须使用 CSRF 令牌添加一个隐藏字段。我找不到更好的解决方法,但仍然不确定为什么会发生这种情况,或者只是再次添加令牌来修复它。
  • 我认为这是更好的解决方案,因为它尊重单一职责原则并让事情变得清晰,每个按钮执行自己的操作,保持控制器中的逻辑简单。
【解决方案3】:

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

<% 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],具体取决于按下的是哪一个。

【讨论】:

  • 仍然没有将视图与控制器分离。
  • 是的,如果解耦意味着避免动作中的某些逻辑以便路由到您正确的最终动作,那么它们仍然是耦合的。我的意思是,如果您在该逻辑中使用 name 属性,您的控制器将独立于按钮上显示的内容。谢谢,已编辑
  • 在 i18n 情况下,这似乎比公认的要好,因为显示“值”,如果您显示 Unicode 字符,它会变得混乱。
  • 但是参数没有被允许通过。我正在使用 simple_form gem。有没有相关性。
  • 这不会将视图与控制器分离,但至少它将显示的文本与控制器分离。更好的海事组织。
【解决方案4】:

与@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

请注意,我避免使用值并改用输入名称,因为提交按钮值通常是国际化/翻译的。另外,我会避免过多地使用它,因为它会很快使您的路线文件变得混乱。

【讨论】:

    【解决方案5】:

    我们在 rails 中使用 advanced constraints 解决了问题。

    这个想法是拥有相同的路径(因此具有相同的命名路由和动作),但具有路由到不同动作的约束。

    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_matching.

    【讨论】:

      【解决方案6】:

      一个老问题,但由于我一直在处理同样的情况,我想我会发布我的解决方案。我使用控制器常量来避免在控制器逻辑和视图按钮之间引入差异。

      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”是什么意思?
      • 这比在路由中使用约束更可取吗?谢谢!
      • @Scott:i18n 的意思是“国际化”——基本上,你将如何支持多种语言。我还没有真正研究过它,所以我对它的工作原理或实现方式不是很熟悉。
      • @Angela - 可能不是 :) 实际上,在重构我的代码之后,我只是创建了多个表单,每个表单都有不同的操作,而不是包含一堆不相关表单的单个整体表单。
      【解决方案7】:

      感谢nested_form_fields,我的表单上的提交按钮数量不定,因此仅使用名称对我来说是不够的。我最终在表单中包含了一个隐藏的输入字段,并在按下某个表单提交按钮时使用 Javascript 填充它。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-10
        • 2011-03-20
        • 2016-04-06
        • 2016-09-24
        • 1970-01-01
        • 2017-10-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多