【问题标题】:Ajax Forms with rails 4, :remote => true and JQuery - What are the Best Practices?带有 rails 4、:remote => true 和 JQuery 的 Ajax 表单 - 最佳实践是什么?
【发布时间】:2014-11-11 21:22:52
【问题描述】:

我正在 Rails 4 中使用 ajax 构建一个多部分表单,我想知道我这样做是否正确。

这是我目前使用的策略。我的控制器叫做 ajaxform。

在我的 ajaxform_controller.rb 的顶部,在类定义中而不是在函数中,我有

respond_to :js

第一阶段

从另一个视图到该工具的链接使用

get 'ajaxform' => 'ajaxform#stage_one'

主视图“stage_one.html.erb”包含表单的第一部分,使用 :remote => true 选项通过 form_tag 定义。同名控制器方法为空。

<%= form_tag("/stage_one_form", method: 'post', :remote => true) do %>
    <%= label_tag(:stage_one_data, "Stage one data: ") %>
    <%= select_tag(:stage_one_data, options_for_select([['Option1', :option1], ['Option2', :option2], ['Option3', :option3]], 1)) %>
    <%= submit_tag 'Next' %><span id="stage_two_waiting" style="display:none;"></span>
<% end %>
<br />
<span id="stage_two_form" style="display:none;"></span>

隐藏跨度标签:在“下一步”按钮的右侧,有一个隐藏跨度标签,其 id 属性为“stage_two_waiting”。当表单提交时,routes.rb 告诉应用程序进入 stage_two_waiting,这将在 span 标签内显示“请稍候...”消息。 在它下面,还有另一个隐藏的跨度标签,其 id 为“stage_two_form”——这是我们从基于第一阶段选择的查询中获取数据后,我们表单的第二阶段将呈现的地方。

第二阶段

post 'stage_one_form' => 'ajaxform#stage_two_waiting'

stage_two_waiting 是包含两个文件的局部视图:stage_two_waiting.js.erb 和 _stage_two_waiting.html.erb。 html.erb文件开头的下划线表示是部分的,但是同名的.js.erb文件没有下划线。

这里是 stage_two_waiting.js.erb

$('span#stage_two_waiting').append("<%= escape_javascript (render partial: 'stage_two_waiting') %>");
$('span#stage_two_waiting').slideDown(350);
$("#trigger_stage_two").trigger("submit.rails");

前两行导致 _stage_two_waiting.html.erb 在第一阶段表单的 span 标记内呈现,具有漂亮的 slideDown 效果。第三行触发 _stage_two_waiting.html.erb 中隐藏表单的提交,它将数据传递到下一个表单并路由到它:

<%= form_tag("/stage_two_waiting_form", :id => 'trigger_stage_two', method: 'post', :remote => true) do %>
    <!-- hidden field to pass the selected information from stage 1 on to stage 2-->
    <%= hidden_field_tag(:stage_one_data, params[:stage_one_data])%>
<% end %>

当 JQuery 触发隐藏表单提交时,routes.rb 指定实际第二阶段表单本身的位置:

post 'stage_two_waiting_form' => 'ajaxform#stage_two_form'

stage_two_form 也有两个文件,stage_two_form.js.erb 和 _stage_two_form.html.erb。在这些文件可以呈现之前,控制器需要进行 REST 调用。

def stage_two_form
    stage_one_selected_option = params[:stage_one_data]    
    rest_response = `curl -k -X GET https://myrestservice.net/v1/myfunction/#{stage_one_selected_option}`

    #convert the rest response into an array of select list options for the stage two form
    @stage_two_options = Array.new
    @stage_two_options.push "parsed information from rest_response would go in here"

    respond_with(@stage_two_options) do |format|
      format.js
    end
end

stage_two_form.js.erb 看起来像这样:

$('span#stage_two_form').append("<%= escape_javascript (render partial: 'stage_two_form') %>");
$('span#stage_two_form').slideDown(350);

在控制器方法完全执行完成之前,路由不会触发此 Javascript。在那之前,用户只会看到“请稍候...”消息。方法完成后,javascript 会呈现表单及其实例变量 @stage_two_options

  <%= form_tag("/stage_two_form", method: 'post', :remote => true) do %>
      <%= label_tag(:stage_two_data, "Available options based on stage one: ") %>
      <%= select_tag(:stage_two_data, options_for_select(@stage_two_options.transpose[0].collect)) %>
      <%= hidden_field_tag(:stage_one_data, params[:stage_one_data]) %>
      <%= submit_tag 'Next' %><span id="stage_three_waiting" style="display:none;"></span>
  <% end %>
  <br />
  <span id="stage_three_form" style="display:none;"></span>

hidden_​​field_tag 会传递在第一阶段所做的选择,以防第三阶段需要它。如果以后需要,我们还可以编写第二个 hidden_​​field_tag 来传递控制器方法提供的整个选项数组。

      <%= hidden_field_tag(:stage_two_options, @stage_two_options) %>

发布此表单时的路线如下:

post 'stage_two_form' => 'ajaxform#stage_three_waiting'

进一步的阶段

第三阶段也一共有四个文件,两个用于等待消息,两个用于表单。它们是 stage_three_waiting.js.erb、_stage_three_waiting.html.erb、stage_three_form.js.erb 和 _stage_three_form.html.erb。和以前一样,第三阶段的等待部分在其中有一个隐藏的形式,可以传递第二阶段所需的任何参数。第三阶段的两个部分都呈现在 _stage_two_form.html.erb 中的 span 标记内,并带有 JQuery 附加。这个过程可以根据需要持续多个阶段。

问题

在我看来,多部分表单的每个“阶段”都应该依赖位于最多五个不同位置的代码:两个文件用于“请稍候...”消息,另外两个文件对于表单本身,控制器方法内的代码。需要将数据从一个阶段传递到下一个阶段作为隐藏字段中的参数似乎也很奇怪。它很笨拙,并且由于某种原因,表单在 safari 8 中也无法正常工作,因为它保持路由为 HTML 并忽略 JQuery。

结论

我怀疑我没有以最优雅的方式执行此操作 - 任何帮助将不胜感激。

问题是——是否可以使用这些隐藏字段将参数中的数据从一个局部视图传递到另一个局部视图到另一个视图,或者是否有一种更简洁的方法来做到这一点,它不涉及这么多不同的文件和隐藏字段?

【问题讨论】:

  • 这种问题更适合codereview.stackexchange.com,因为您正在寻找有关您的代码的审查和一般建议。不是针对特定问题的答案。
  • -1 Stackoverflow 不是论坛 - 我们没有主题 - 我们有问题和答案,而且这个问题不太适合格式。
  • 也许我不够清楚 - 具体问题是:最好的方法是什么?我做对了吗?

标签: jquery ruby-on-rails ajax partial


【解决方案1】:

全局变量 除了通过隐藏字段将参数从局部视图传递到局部视图之外,另一种方法是在控制器中使用全局变量(以 $ 开头)。这使得它们可用于任何控制器的视图及其在控制器本身中的所有功能

ajaxform_controller.rb

def stage_two_form
    # define a global variable that we expect to use later from a different partial
    $global_selected_option = params[:stage_one_data]
    ...
end

def stage_three_form
    # the "stage_three_form" method still has access to the global variable
    $stage_three_option = $global_selected_option + ' In stage 3'
end

_stage_three_form.html.erb

<!-- _stage_three_form.html.erb and all other partial views have access to globals -->
<p>The original option was: <%= $global_selected_option %></p>
<%= form_tag("/stage_three_form", method: 'post', :remote => true) do %>
      <%= label_tag(:stage_three_selection, "#{$stage_three_option}") %>
      ...
<% end %>

【讨论】:

  • 这样的全局变量能在 Heroku 这样的 PaaS 上工作吗?另一种选择是将变量放在会话 cookie 中。
  • 我无法真正谈论 PaaS 和全局变量 - 但 Ruby 社区似乎通常不赞成全局变量。
猜你喜欢
  • 2012-01-01
  • 2014-03-19
  • 1970-01-01
  • 2015-01-04
  • 1970-01-01
  • 2015-08-26
  • 1970-01-01
  • 1970-01-01
  • 2015-01-17
相关资源
最近更新 更多