JavaScript 的 Fetch 可以用来更新 Rails 控制器中的参数值吗?

Posted

技术标签:

【中文标题】JavaScript 的 Fetch 可以用来更新 Rails 控制器中的参数值吗?【英文标题】:Can JavaScript's Fetch be used to update param values in a Rails controller? 【发布时间】:2020-01-17 15:16:38 【问题描述】:

使用 javascript、Rails 以及 DataTables API 设置用于跟踪任务活动的报告界面。现在,我正在尝试通过使用 JavaScript 连接界面的现有日期选择按钮(例如,今天、过去 7 天、过去 30 天等)来弄清楚如何用新数据更新面向客户端的数据表在 Rails 控制器中切换不同的time_range 参数值。

在控制器代码中,在这些参数值之间切换意味着更新服务器端查询的date_range 参数。此参数随后通过控制 db 命令的 WHERE 子句中指定的内容来过滤服务器端查询返回的数据。

我的问题是我在 JavaScript 中设置的获取请求显然无法对切换哪些参数值被传递到控件产生任何影响flow 在控制器的index 方法的顶部。


编辑

这是开发工具中的网络窗格在单击“昨天”和“过去 7 天”日期选择按钮后显示的内容。这些点击似乎正在运行成功的获取请求:

我应该在这里指出的另一件事是,从网络工具的预览返回和查看的数据对于我的“昨天”提取与我的“过去 7 天”提取的相同 .发出我的 fetch 请求的全部意义在于,它通过与 Rails 控制器中编写的查询的交互来检索针对界面中选择的日期范围定制的 json 数据结构。

在那里,在控制流块之后使用puts 输出我的@date_range 变量的值(用破折号块分隔此调试方面),Rails 控制台准确地报告了问题 - 我的客户端代码没有按预期更新@date_range 的值,但是,它确实在顶部显示Parameters 散列,指出对传递给@的值的更新可见性987654330@:

Started GET "/task_reporting?time_range=yesterday" for ::1 at 2019-09-17 10:01:21 -0400
Processing by TaskReportingController#index as JSON
  Parameters: "time_range"=>"yesterday", "task_reporting"=>
  User Load (2.6ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
      ---------------------------------------------------------
      The value of @date_range is >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH)
      ---------------------------------------------------------
   (13.1ms)          SELECT tasks.id, tasks.name, users.name, DATE_FORMAT(MAX(kits.updated_at), '%a. %b %d, %Y'),
        COUNT(DISTINCT(DATE(kits.updated_at))), COUNT(DISTINCT(kits.id))
        FROM kits
        JOIN tasks ON tasks.id = kits.task_id JOIN users ON users.id = kits.user_id JOIN annotations ON annotations.kit_id = kits.id
        WHERE kits.updated_at >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH)
        GROUP BY tasks.id, users.name

Completed 200 OK in 39ms (Views: 1.3ms | ActiveRecord: 15.7ms)


Started GET "/task_reporting?time_range=last_seven_days" for ::1 at 2019-09-17 10:02:29 -0400
Processing by TaskReportingController#index as JSON
  Parameters: "time_range"=>"last_seven_days", "task_reporting"=>
  User Load (4.3ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
      ---------------------------------------------------------
      The value of @date_range is >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH)
      ---------------------------------------------------------
   (1.7ms)          SELECT tasks.id, tasks.name, users.name, DATE_FORMAT(MAX(kits.updated_at), '%a. %b %d, %Y'),
        COUNT(DISTINCT(DATE(kits.updated_at))), COUNT(DISTINCT(kits.id))
        FROM kits
        JOIN tasks ON tasks.id = kits.task_id JOIN users ON users.id = kits.user_id JOIN annotations ON annotations.kit_id = kits.id
        WHERE kits.updated_at >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH)
        GROUP BY tasks.id, users.name

Completed 200 OK in 18ms (Views: 1.1ms | ActiveRecord: 6.0ms)

结束编辑


代码 cmets 中描述的更多信息...

我的 JavaScript (task_reporting.js.coffee) 是从该工具的视图代码末尾调用的:

Rails 视图(简化)

index.html.haml:

-content_for :action_specific_js do
  != "task_reporting.init()"

JavaScript (jQuery + CoffeeScript)

window.task_reporting = ( ->
    // Important: initializes DataTable, loading default, thirty-day date-range data
    init: () ->
          this.init_datepicker_button()
          this.init_daterange_buttons()
          $("#tasks_report").DataTable
              paging: false
              scrollY: "400px"
              scrollCollapse: true
              sAjaxSource: "/task_reporting"
              order: [[1, "asc"]]
    // Important: Believe the problem is here.
    // Method should send fetch request to server-side, altering query parameters in the controller
    render_table: (passed_time_range) ->
          query_params = "?time_range=#passed_time_range"
          console.log(query_params)

          fetch("/task_reporting#query_params",
              method: 'GET'
              headers:
                  "content-type": "application/json"
                  "Accept": "application/json"
              credentials: "same-origin"
          )
          .then (response) -> response.json()
          .then (data) ->
            console.log(data)
            $("#tasks_report").DataTable().clear().rows.add(data).draw
          .catch((e) -> console.error("Error:", e))

    // Not important: sets listener on date-range button (undeveloped)
    init_datepicker_button: ->
          dateRangeButton = $(".datepickerContainer > button#daterange")
          dateRangeButton.on "click", this.init_dropdown

    // Sets listeners on individual date range buttons, calling pass_time_range to set param value in fetch
    init_daterange_buttons: ->
          rangeItemButton = $(".range-item")
          rangeItemButton.on "click", this.pass_time_range

    init_dropdown: ->
          console.log "dropdown here"

    // Handles event on daterange buttons
    // Passes param value to render_table according to button's class name
    pass_time_range: (e) ->
          e.preventDefault()
          // console.log e
          clickedTimeRange = e.target.className
          if clickedTimeRange.includes "today"
              task_reporting.render_table("today")
          else if clickedTimeRange.includes "yesterday"
              task_reporting.render_table("yesterday")
          else if clickedTimeRange.includes "last_seven_days"
              task_reporting.render_table("last_seven_days")
          else if clickedTimeRange.includes "last_thirty_days"
              task_reporting.render_table("last_thirty_days")
          else if clickedTimeRange.includes "custom_range"
              task_reporting.render_table("custom_range")
)()

Rails 控制器

class TaskReportingController < ApplicationController
  
  before_filter :authenticate
  
  def index
    
      # Checks param value and updates @date_range accordingly
      if params[:time_range] == "range_today"
        @date_range = '>= CURDATE()'
      elsif params[:time_range] == "range_yesterday"
        @date_range = '>= DATE_SUB(CURDATE(), INTERVAL 1 DAY)'
      elsif params[:time_range] == "range_last_seven_days"
        @date_range = '>= DATE_SUB(CURDATE(), INTERVAL 7 DAY)'
      else params[:time_range] == "range_last_thirty_days"
        @date_range = '>= DATE_SUB(CURDATE(), INTERVAL 1 MONTH)'
      end
    
    # My fetch obviously fails to influence the :time_range param value
    # as the default thirty day range is always output here
    print <<-EOM
      ############
      The value of @date_range is #@date_range
      ############
    EOM

    # heredoc query
    query = <<-SQL
        SELECT tasks.name, users.name, DATE_FORMAT(MAX(kits.updated_at), '%a. %b %d, %Y'), 
        COUNT(DISTINCT(DATE(kits.updated_at))), COUNT(DISTINCT(kits.id)) 
        FROM kits
        JOIN tasks ON tasks.id = kits.task_id JOIN users ON users.id = kits.user_id JOIN annotations ON annotations.kit_id = kits.id
        WHERE kits.updated_at #@date_range
        GROUP BY tasks.id, users.name
      SQL
        
    query_result = Kit.connection.execute(query).to_a

    respond_to do |format|
      format.html
      format.json  render json: "aaData": query_result 
    end
  end
end

寻找错误的方向。欢迎就使用这样的 fetch 是否是使用 DataTables API 从客户端更新表数据的正确方法提出任何建议。

【问题讨论】:

在 fetch 调用 ajax 时显示日志,params 里面是什么?还要检查浏览器的开发工具,网络选项卡也应该为您提供请求的信息。 谢谢,我在这方面相对较新(尤其是在 Rails 方面),还没有考虑从网络控制台查看我的请求。虽然这很有帮助,但我可以在编写请求时看到有关请求的更多明确信息,但我仍然不确定如何将查询字符串参数从我的 JS 传递到 Rails 控制器。无论如何,我已经用请求的屏幕截图以及 Rails 日志输出更新了我的帖子。感谢您的建议。 【参考方案1】:

您的参数值在您的日志中是“昨天”或“last_seven_days”,但您的代码会检查“range_yesterday”或“range_last_seven_days”。这就是它失败并回到默认范围的原因。

执行以下操作:

query_params = "?time_range=range_#passed_time_range"

或更改您的支票以与“昨天”而不是“range_yesterday”进行比较

【讨论】:

哈哈哈,是的,我的 Rails 控制器中参数值的名称检查差异完全是它。遗憾的是,在我的 JS 中没有 range_ 的情况下更新值以传递到 query_params 并忽略在我的 Rails 控制器中进行相同的更新后,过去三天一直在这个问题上挣扎。 获取请求现在完全按照需要工作。非常感谢使用 DataTables 更新实际表。 非常感谢您花时间阅读我的问题和我的代码,并指出这是命名上的简单区别。 ?‍♂️ 必须想出一个可靠的方法来避免将来犯此类错误

以上是关于JavaScript 的 Fetch 可以用来更新 Rails 控制器中的参数值吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何在fetch then / response函数中访问请求对象

使用 JavaScript Axios/Fetch。你能禁用浏览器缓存吗?

分别使用 XHRjQuery 和 Fetch 实现 AJAX

JavaScript中fetch获取后台数据

使用 Fetch

javascript fetch 响应 403 错误