Rails 如何将控制器中的@products 传递给另一个 Java 脚本

Rails how to get @products in the controller to be passed to another Java Script

我从另一个自定义开发的 class 获取我的产品索引,该索引被传递给 index.html.erb 以使用 DataTables 和 [=] 以 table 格式呈现16=] 在 index.html.erb 页面中。到目前为止,这段代码运行良好,在 server side 上执行 paginationsortingfiltering,但现在我想从JSONDataTables 使用,我想在 ProductsController 中处理它,然后将其作为 @hash 传递给另一个 JavaScript 最终呈现 @markers@productsGoogle Map.

这是我的代码:

products_controller.rb

...
  def index
    respond_to do |format|
      format.html
      format.json { render json: ProductsDatatable.new(view_context) }
    end
  end
...

index.html.erb

    <div class="container-fluid">
      <h1>Products</h1>

      <table id="products" width="100%" class="display cell-border compact hover order-column row-border stripe" data-source="<%= products_url(format: "json") %>">
        <thead>
          <tr>
            <th style="text-align: center">Product ID</th>
            <th style="text-align: center">Product Name</th>
            <th style="text-align: center">Category</th>
            <th style="text-align: center">Release Date</th>
            <th style="text-align: center">Price</th>
            <th style="text-align: center">Created At</th>
            <th style="text-align: center">Updated At</th>
          </tr>
        </thead>
        <tbody>
        </tbody>
      </table>
    </div>

    <script>
      $(document).ready(function() {
        $('#products').dataTable({
          sPaginationType: "full_numbers",
          bJQueryUI: true,
          bProcessing: true,
          bServerSide: true,
          sAjaxSource: $('#products').data('source'),
          sDom: 'CRlfrtip',
          bStateSave: true,
          responsive: true
        })
      } );
    </script>

Custom class fetching the records from server with pagination, sorting and filtering.

`/datatables/products_datatables.rb`

class ProductsDatatable
  delegate :params, :link_to, :number_to_currency, to: :@view

  def initialize(view)
    @view = view
  end

  def as_json(options = {})
    {
      sEcho: params[:sEcho].to_i,
      iTotalRecords: Product.count,
      iTotalDisplayRecords: products.total_entries,
      aaData: data
    }
  end

private

  def data
    products.map do |product|
      [
        link_to(product.id, product),
        product.product_name,
        product.category,
        product.release_date.strftime("%Y-%m-%d"),
        number_to_currency(product.price),
        product.created_at.strftime("%Y-%m-%d %I:%M%p"),
        product.updated_at.strftime("%Y-%m-%d %I:%M%p")
      ]
    end
  end

  def products
    @products ||= fetch_products
  end

  def fetch_products #this version genertes more optimized queries for the db
    if params[:sSearch].present?
      products = Product
      .where("product_name like :search
        or category like :search
        or date_format(release_date, '%Y-%m-%d') like :search
        ", search: "%#{params[:sSearch]}%"
      )
      .order("#{sort_by}")
      .page(page).per_page(per_page)
    else
      products = Product
      .order("#{sort_by}")
      .page(page).per_page(per_page)
    end
    products
  end

  def page
    params[:iDisplayStart].to_i/per_page + 1
  end

  def per_page
    params[:iDisplayLength].to_i > 0 ? params[:iDisplayLength].to_i : 10
  end

  def sort_by
    columns = %W[id product_name category release_date price created_at updated_at]
    s = String.new
    if params[:iSortCol_0].present?
      s = s + "," + columns[params[:iSortCol_0].to_i] + " " + sort_direction(params[:sSortDir_0])
    end
    if params[:iSortCol_1].present?
      s = s + "," + columns[params[:iSortCol_1].to_i] + " " + sort_direction(params[:sSortDir_1])
    end
    if params[:iSortCol_2].present?
      s = s + "," + columns[params[:iSortCol_2].to_i] + " " + sort_direction(params[:sSortDir_2])
    end
    if params[:iSortCol_3].present?
      s = s + "," + columns[params[:iSortCol_3].to_i] + " " + sort_direction(params[:sSortDir_3])
    end
    if params[:iSortCol_4].present?
      s = s + "," + columns[params[:iSortCol_4].to_i] + " " + sort_direction(params[:sSortDir_4])
    end
    if s.empty?
      s = columns[params[:iSortCol_0].to_i] + " " + sort_direction(params[:sSortDir_0])
    end
    if s[0] == ","
      s.slice!(0)
    end
    s
  end

  def sort_direction (n)
    n == "desc" ? "desc" : "asc"
  end
end

我如何在控制器中获取 @products,这样当用户在页面索引页面之间导航时,@products 的内容相应地改变?

如果我没看错的话,你不能完全按照你的建议去做。您正在处理 2 个请求。您可以很容易地将 ProductsDataTable class 中的数据甚至 JSON 作为控制器实例变量公开,并将其写入模板脚本标记中的 JavaScript 变量中,但因为它坐下,您仍然会在数据 table 初始化后立即收到第二个电话以获取 JSON。

看来您尝试做的事情可以在 JavaScript 中完全完成。您可以像 https://datatables.net/reference/option/initComplete 一样将 initComplete 选项传递给数据 table。这将在检索到数据时调用传递的回调。

这将使 table 中的 JSON 数据在传递给指定回调函数的 json 变量中可用。然后您可以在回调中初始化您的地图。

这是我期待的解决方案。

在控制器中,一行代码:

@json = ProductsDatatable.new(view_context).as_json

这使得数据可用于进一步处理,如下例所示。

您可以显示整个 JSON 对象:

<%= @json %>

然后根据需要进一步对这个 json 对象进行切片和切块。

Total Records Count: <%= @json[:iTotalRecords] %>
aaData class: <%= @json[:aaData].class %>
aaData count: <%= @json[:aaData].count %>
aaData first class: <%= @json[:aaData].first.class %>
aaData first content: <%= @json[:aaData].first %>
aaData first content product Product ID (link): <%= @json[:aaData].first[0] %>
aaData first content product Product Name: <%= @json[:aaData].first[1] %> 

现在的问题是当页面改变时(通过table 分页)只有前 10 条记录可见,而不是第 5、7、10 页等的当前 10 条记录

即使在我收到建议并重新编写代码后,这个老问题仍然存在。当我使用数据 table 的导航按钮移动到另一页时,第一页、最后一页、上一页、下一页等,table 内容相应更改,但 the map shows always the same first 10 products.

当 table 中呈现的内容发生变化时,Google 地图永远不会发生变化。我该如何解决这个问题?不知道如何更改我的代码以使其正常工作? 我需要的是在 @products 随分页更改时更改 @hash 内容。如何做到这一点?

products_controller.rb

class ProductsController < ApplicationController
  before_action :set_product, only: [:show, :edit, :update, :destroy]

  # GET /products
  # GET /products.json
  def index

    if params[:sSearch].present?
      @products = Product
      .where("product_name like :search
        or category like :search
        or date_format(release_date, '%Y-%m-%d') like :search
        ", search: "%#{params[:sSearch]}%"
      )
      .order("#{sort_by}")
      .page(page).per_page(per_page)
    else
      @products = Product
      .order("#{sort_by}")
      .page(page).per_page(per_page)
    end

    @hash = Gmaps4rails.build_markers(@products) do |product, marker|
      marker.lat product.lat || 0.0
      marker.lng product.lng || 0.0
      title = product.product_name
      marker.infowindow make_content_for_infowindow(product)
      marker.json({:title => title})
    end

    @products_data = @products.map do |product|
      [
        product.id,
        product.created_at.strftime("%Y-%m-%d %H:%M:%S %Z"),
        product.updated_at.strftime("%Y-%m-%d %H:%M:%S %Z"),
        product.product_name,
        product.category,
        product.release_date ? product.release_date.strftime("%Y-%m-%d") : nil,
        product.price,
        product.current_location,
        product.lat,
        product.lng
      ]
    end

    @json = {
      "sEcho"               => params[:sEcho].to_i,
      "iTotalRecords"       => @products.count,
      "iTotalDisplayRecords"=> @products.total_entries,
      "aaData"              => @products_data.as_json
    }

    respond_to do |format|
      format.html
      format.json { render :json=> @json }
    end
  end

  # GET /products/1
  # GET /products/1.json
  def show
  end

  # GET /products/new
  def new
    @product = Product.new
  end

  # GET /products/1/edit
  def edit
  end

  # POST /products
  # POST /products.json
  def create
    @product = Product.new(product_params)

    respond_to do |format|
      if @product.save
        format.html { redirect_to @product, notice: 'Product was successfully created.' }
        format.json { render :show, status: :created, location: @product }
      else
        format.html { render :new }
        format.json { render json: @product.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /products/1
  # PATCH/PUT /products/1.json
  def update
    respond_to do |format|
      if @product.update(product_params)
        format.html { redirect_to @product, notice: 'Product was successfully updated.' }
        format.json { render :show, status: :ok, location: @product }
      else
        format.html { render :edit }
        format.json { render json: @product.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /products/1
  # DELETE /products/1.json
  def destroy
    @product.destroy
    respond_to do |format|
      format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_product
      @product = Product.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def product_params
      params.fetch(:product, {})
    end

    def page
      params[:iDisplayStart].to_i/per_page + 1
    end

    def per_page
      params[:iDisplayLength].to_i > 0 ? params[:iDisplayLength].to_i : 10
    end

    def sort_by
      #these should be enumerated in the same order as they appear rendered in the table from left to right
      columns = %W[id created_at updated_at product_name category release_date price current_location lat lng]
      s = String.new
      if params[:iSortCol_0].present?
        s = s + "," + columns[params[:iSortCol_0].to_i] + " " + sort_direction(params[:sSortDir_0])
      end
      if params[:iSortCol_1].present?
        s = s + "," + columns[params[:iSortCol_1].to_i] + " " + sort_direction(params[:sSortDir_1])
      end
      if params[:iSortCol_2].present?
        s = s + "," + columns[params[:iSortCol_2].to_i] + " " + sort_direction(params[:sSortDir_2])
      end
      if params[:iSortCol_3].present?
        s = s + "," + columns[params[:iSortCol_3].to_i] + " " + sort_direction(params[:sSortDir_3])
      end
      if params[:iSortCol_4].present?
        s = s + "," + columns[params[:iSortCol_4].to_i] + " " + sort_direction(params[:sSortDir_4])
      end
      if s.empty?
        s = columns[params[:iSortCol_0].to_i] + " " + sort_direction(params[:sSortDir_0])
      end
      if s[0] == ","
        s.slice!(0)
      end
      s
    end

    def sort_direction (n)
      n == "desc" ? "desc" : "asc"
    end

    def make_content_for_infowindow(product)
      s = String.new
      s << "Name: "
      s << product.product_name
      s << " ID: "
      s << product.id.to_s
      s << " Category: "
      s << product.category
      s << " Price: "
      s << product.price.to_s
      s
    end
end

index.html.erb

div class="container-fluid">
  <h1>Products</h1>

  <table id="products" width="100%" class="display cell-border compact hover order-column row-border stripe" data-source="<%= products_url(format: "json") %>">
    <thead>
      <tr>
        <th style="text-align: center">Product ID</th>
        <th style="text-align: center">Created At</th>
        <th style="text-align: center">Updated At</th>
        <th style="text-align: center">Product Name</th>
        <th style="text-align: center">Category</th>
        <th style="text-align: center">Release Date</th>
        <th style="text-align: center">Price</th>
        <th style="text-align: center">Current Location</th>
        <th style="text-align: center">Latitude</th>
        <th style="text-align: center">Longitude</th>
      </tr>
    </thead>
    <tbody>
    </tbody>
  </table>
</div>

<div class="row-fluid">
  <div id="map" style='width: 100%; height: 500px; border: 1px solid black;'>
  </div>
</div>

<br>

<script>
  $(document).ready(function() {
    $('#products').dataTable({
      sPaginationType: "full_numbers",
      bJQueryUI: true,
      bProcessing: true,
      bServerSide: true,
      sAjaxSource: $('#products').data('source'),
      sDom: 'CRlfrtip',
      bStateSave: true,
      responsive: true
    })
  } );
</script>

<script type="text/javascript">
  buildMap (<%=raw @hash.to_json %>);
</script>

要渲染的 Google 地图的咖啡脚本 gmaps_google.js.coffee

class RichMarkerBuilder extends Gmaps.Google.Builders.Marker #inherit from builtin builder
  #override create_marker method
  create_marker: ->
    options = _.extend @marker_options(), @rich_marker_options()
    @serviceObject = new RichMarker options #assign marker to @serviceObject

  rich_marker_options: ->
    marker = document.createElement("div")
    marker.setAttribute 'class', 'marker_container'
    marker.innerHTML = @args.title
    _.extend(@marker_options(), { content: marker })

  infobox: (boxText)->
    content: boxText
    pixelOffset: new google.maps.Size(-140, 0)
    boxStyle:
      width: "400px"

  # override method
  create_infowindow: ->
    return null unless _.isString @args.infowindow
    boxText = document.createElement("div")
    boxText.setAttribute("class", 'marker_info_box') #to customize
    boxText.innerHTML = @args.infowindow
    @infowindow = new InfoBox(@infobox(boxText))

@buildMap = (markers)->
    handler = Gmaps.build 'Google', { builders: { Marker: RichMarkerBuilder} } #dependency injection

    #then standard use
    handler.buildMap { provider: {}, internal: {id: 'map'} }, ->
      markers = handler.addMarkers(markers)
      handler.bounds.extendWith(markers)
      handler.fitMapToBounds()