Rails 4:如何使用AJAX更新索引页面

时间:2014-10-03 17:27:32

标签: javascript jquery ruby-on-rails ajax json

我无法相信我还没有找到其他问题来回答这个问题,但我已经搜索过高低,无法找到真正回答我问题的任何问题。

基本上,我有一个费用页面,我想在表格中显示给定月份内的所有费用。我有一个月份和年份选择,现在我通过向网址添加月份和年份参数并使用一些小的javascript转到该href。我想避免页面刷新,只是在更改选择框值时更新表。据我了解,因为我没有使用表单,所以我不能使用:remote => true因此必须使用AJAX。我的研究让我设置如下。

这是我带有AJAX调用的JQuery:

$(document).ready(function() {

    $('#monthSelect').change(function() {
        // alert("yay");

        var m = $(this).val();
        var y = $("#yearSelect").val();
        $.ajax({
            type: "GET",
            url: "/expenses",
            data: { month : m, year : y } ,
            success: function(data) {

            },
            dataType: "json"
        });
    });
});

#monthSelect#yearSelect是我的两个选择框的ID。这部分似乎有效。当我更改月份选择框的值时,它会在"/expenses"中将正确的月份和年份的GET请求发送到params

/expenses自然会路由到我的index操作。这是我感到困惑的部分。

def index
    @expenses = # Code that gets the expenses that correspond to the month and year in the params.
    # This part works. I've tested it and @expenses are all the records I want

    respond_to do |format|
        format.html
        format.json { render json: @expenses, status: :ok } #?????
    end
end

所以我想我的主要问题是json部分是如何工作的,以及我是否应该在AJAX中使用json。我所看过的教程和视频似乎都暗示在json中获取响应是你应该这样做的方式,但我不确定是什么"渲染json&#34 ;甚至意味着。我的理解是传递@expenses,它基本上会呈现@expenses.to_json返回的内容,这只是一堆json,键值对。我该怎么做并制作一张桌子?

我的一位朋友已经做了很多AJAX,但没有Ruby on Rails说我可以在AJAX成功函数中编写HTML

...
success: function(data) {
    $("#expenses-table").html("HTML of my Table");
}

但是,这似乎并不像Rails方式,只是放了一堆只是将HTML放入字符串的Javascript代码。如果我只是将HTML放在AJAX中,那么render: json会怎么做?我确实在Stack Overflow上看到了答案

success: function(data) {
    $("#expenses-table").html(data);
}

这将是不错的,因为数据确实拥有我想要的所有json。但显然这并不像那样。

所以,如果我能对这整个混乱局部得到一些澄清,如果我甚至用JSON接近它,或者我应该在AJAX中编写HTML,那将非常感激。

编辑1: 这是我目前的表格代码:

<table id="expenses">
    <thead>
      <tr>
        <th>Description</th>
        <th>Amount</th>
        <th>User</th>
        <th>Date</th>
        <th colspan="3"></th>
      </tr>
    </thead>

    <tbody>
      <% @expenses.each do |expense| %>
        <tr id="expenses-row">
          <td><%= expense.description %></td>
          <td><%= number_to_currency expense.amount %></td>
          <td><%= expense.user.name %></td>
          <td><%= expense.date.strftime("%m/%Y") %></td>
          <td><%= link_to 'Show', expense %></td>
          <% if expense.user == @user %>
            <td><%= link_to 'Edit', edit_expense_path(expense) %></td>
            <td><%= link_to 'Delete', expense, method: :delete, data: { confirm: 'Are you sure?' } %></td>
          <% else %>
            <td></td>
            <td></td>
          <% end %>
        </tr>
      <% end %>
    </tbody>
  </table>

基本上只是标准的脚手架表。大多数变化只是css。这里是json对于一条记录的样子

[{"id":8,"description":"This is a test","amount":"35.0","user_id":1,"date":"2014-10-01","created_at":"2014-10-03T07:07:53.412Z","updated_at":"2014-10-03T07:07:53.412Z","receipt_file_name":null,"receipt_content_type":null,"receipt_file_size":null,"receipt_updated_at":null}]

我想我更喜欢ERB / ruby​​中的代码,因为我更熟悉它。虽然使用json几乎感觉有点无意义,因为@expenses是我已经在我当前的表代码中使用的。但是,不会渲染:file&#34; expenses_table.html.erb&#34;刷新整个页面?或者它如何知道在哪里渲染该表并替换以前的表?回复format.js并制作index.js.erb几乎听起来更容易,因为我知道我可以用部分代替html。但是我没有尝试过,因为我见过的所有例子都回复format.json。如果听起来我只是困惑而且不知道发生了什么,因为我是。我只需要澄清一下。谢谢你的帮助!

1 个答案:

答案 0 :(得分:3)

  

如果我甚至应该在AJAX中使用json。

您可以使用任何类型的String。问题是解析javascript端的字符串以提取每个数据。例如,您可以使用String响应,例如:

"exp1=10, exp2=20, exp3=30"

然后你的javascript代码可以将字符串拆分为&#34;,&#34;,然后&#34; =&#34;签名,然后使用碎片创建一个对象,然后您可以使用该对象来引用数据。另一方面,如果数据是作为JSON字符串发送的,那么您在javascript中所要做的就是:

var obj = JSON.parse(data);

...和obj将类似于ruby Hash,在那里你可以查找键来获取值。

  

所以我想我的主要问题是json部分如何工作......我的   理解是传递@expenses,它基本上会呈现什么   @ expenses.to_json返回,这只是一堆json,键值   对。我该怎么做并制作一张桌子?

那是对的。你使用自己的编程技巧制作表格。

  

我的一个朋友已经做了很多AJAX但没有Ruby on Rails   说我可以在AJAX成功函数中编写HTML

当然,但诀窍仍然是以编程方式获取json中的数据并将其插入到html表中。如果你想走那条路,你可以返回一个完整的表作为响应,这样你就可以在服务器端使用ERB / Nokogiri / ruby​​在表中插入数据。然后你会写一些类似的东西:

@expenses = ....
render :file "/some/dir/expenses_table.html.erb"

另一种方法可能是为表中的每个<td>提供一个id属性,该属性等于json数据中的一个键。然后,您可以使用javascript循环遍历json数据中的每个键,查找具有该id的<td>,然后将该条目替换为与该键对应的json数据中的值。

如果你想要更具体的建议,你必须发布你的表格的一个小例子以及@ expenses.to_json的样子。

顺便说一句,jQuery ajax()函数有太多的功能,所以所有常见的ajax请求都有快捷功能,其中相关的选项为你填写,例如getJSON(),您只需指定网址,数据和成功功能。在这种情况下,这并不能为您节省大量工作,但函数名称可能比#ajax&#39;更具描述性。

对评论问题的回复:

  

我想我的困惑就是这个javascript是什么?这是   原来的AJAX电话?

是。这是您需要编码的唯一JavaScript。

  

那应该在js.erb文件中然后给我   打电话给一些红宝石代码?

没有。如果你想使用ruby创建一些js(这就是erb的用途),你会使用js.erb文件,并且你想要返回js代码作为响应。我们已经确定您不想将javascript作为回复返回。但是,使用rails来设置ajax是非常令人困惑的,所以我可能不理解你的问题的要点。不使用rails生成ajax javascript更简单。

据我所知,如果您在创建html表单时将remote: true放在rails html帮助器中,那么在提交表单时,会向您的rails应用程序发送一个请求,其中包含Accept标头text/javascript。这意味着如果您的操作具有带有format.js行的respond_to块,则该行将执行,并且您可以将一些javascript发送回浏览器,例如一个.js.erb文件,其中包含执行ajax请求以获取费用数据的代码。您的浏览器将立即执行返回的js,它将向服务器发送ajax请求,询问费用数据。请注意,rails方式效率低下:浏览器必须向服务器发送两个请求以完成一个ajax请求:一个请求被发送到服务器以获取将对费用数据发出ajax请求的js代码,然后是浏览器执行ajax代码,该代码向服务器发送另一个请求以获取费用数据。

  

或者我可以在回复中调用javascript文件   至?

在我看来,您希望用一个全新的表替换现有的表,即您不会在现有表中更改两个或三个<td>'s,这应该会使事情变得非常简单。实际上,据我所知,您可以使用与创建现有表相同的erb代码来创建新表。但是,您的索引操作对ajax请求的响应应该只返回表 - 而不是整个页面。这表明你应该将创建表的代码放入一个单独的模板,部分:

views/shared/_table.html.erb

<table id="expenses">
<thead><th>Description</th><th>Amount</th><th>Date</th></thead>
<% @expenses.each do |expense| %>
<tr> 
  <!-- I used an abbreviated Expense model: -->
  <td><%= expense.description %></td>
  <td><%= number_to_currency expense.amount %></td>
  <td><%= expense.date %></td>
</tr>
<% end %>
</table>

然后将部分包含在views/expenses/index.html.erb

<h1>Expenses#index</h1>
<p>Find me in app/views/expenses/index.html.erb</p>

<div>
<select id='expense_day'>
<option value="all">All</option>
<option value="2014-10-5">10/5/14</option>
<option value="2014-10-6">10/6/14</option>
</select>
</div>

<%= render "shared/table" %>   <!-- HERE -->

默认情况下,render()将在名为shared/_table.html.erb的app / views目录中查找部分内容。

这两个文件组合在一起(插入到应用程序布局中)构成了index.html.erb视图。因为该表现在是一个单独的模板,所以只能呈现该表以响应ajax请求:

class ExpensesController < ApplicationController

  def index
    respond_to do |format|
      if request.xhr?  #Is this an ajax request?
        target_date_str = params['target_date']

        if target_date_str == "all"
          @expenses = Expense.all
        else
          @expenses = Expense.where(date: Date.strptime(target_date_str, "%Y-%m-%d"))
        end

        #Render just the table for the ajax request:
        format.html { render partial: "shared/table" }

      else  #then not an ajax request
        @expenses = Expense.all
        format.html   #By default renders index.html.erb 
      end
    end

  end

end

respond_to()查看请求中的Accept标头,并根据它选择匹配的格式:

format.html
format.json
format.xml
etc.  

我检查了jQuery的.get()ajax函数发送的请求,并发送了一个请求,其中text/html的Accept标头为最高优先级,因此format.html将在respond_to阻止。 编辑:我最初编写了索引操作,认为ajax请求会有一个format.json行,非ajax请求会有一个format.html行。但是因为索引操作现在在两种情况下都返回html,所以不需要respond_to()块:

class ExpensesController < ApplicationController
  def index
    if request.xhr?  #if an ajax request...
      target_date_str = params['target_date']

      if target_date_str == "all"
        @expenses = Expense.all
      else
        @expenses = Expense.where(date: Date.strptime(target_date_str, "%Y-%m-%d"))
      end

      render partial: "shared/table"  #Overrides rails default operation which renders index.html.erb

    else  #then not an ajax request
      @expenses = Expense.all  #rails defaults to rendering index.html.erb
    end

  end

以下是我使用的javascript:

<script>

$( document ).ready(function() {
  $("#expense_day").on("change", function() {

    var selected_date = $(this).val();

    $.get( 
      "/expenses", 
      { target_date: selected_date }, 
      function(table) {
        $("#expenses").replaceWith(table)  
      }
    );

  });
});

</script>

    <h1>Expenses#index</h1>
    <p>Find me in app/views/expenses/index.html.erb</p>

    <div>
    <select id='expense_day'>
    <option value="all">All</option>
    <option value="2014-10-5">10/5/14</option>
    <option value="2014-10-6">10/6/14</option>
    </select>
    </div>

    <%= render partial: "shared/table", expenses: @expenses %>

我将javascript放在index.html.erb视图中,这应该清楚地表明javascript是html页面的一部分。但是,您可以从index.html.erb中删除javascript并将其放在另一个文件中 - 但最终js必须找回index.html.erb文件。以下是一些讨论javascript的替代位置的链接:

Why is rails not rendering the .js.erb file?

Best way to add page specific javascript in a Rails 3 app?

Rails有一个名为asset pipeline的东西,它是一种通过删除所有空格将所有javascript和css塞入尽可能少的行的方法。由于文件较小,因此您的浏览器可以更快地加载它们。要利用 minifying ,您必须将assets(即您的javascript和css文件)放入某些目录中。这真的不是你需要担心的事情,因为你的javascript代码不是几千页。