Rails 4: Как обновить страницу индекса с помощью AJAX


Я не могу поверить, что не нашел других вопросов, чтобы ответить на этот вопрос, но я искал повсюду и не могу найти ничего, что действительно отвечает на мой вопрос.

В принципе, у меня есть страница расходов, на которой я хочу отобразить все расходы в данном месяце в таблице. У меня есть выбор месяца и года, и прямо сейчас он работает, добавляя параметры месяца и года в url-адрес и переходя к этому href с небольшим javascript. Однако я хотел бы избежать обновления страницы и просто обновить таблица при изменении значения поля выбора. Как я понимаю, поскольку я не использую форму, я не могу использовать :remote => true и поэтому должен использовать AJAX. Мои исследования привели меня к следующему выводу.

Это мой JQuery с вызовом AJAX:

$(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 - это идентификаторы моих двух полей выбора. Эта часть, кажется, работает. Когда я изменяю значение поля выбора месяца, он отправляет запрос GET в "/expenses" с правильным месяцем и годом в поле выбора месяца. 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, и должен ли я вообще использовать json в AJAX. Учебники и видео, которые я посмотрел, похоже, подразумевают, что получение ответа в json-это способ, которым вы должны это сделать, но я не уверен, что даже означает "рендеринг json". Мое понимание таково, что проходя @expenses, он в основном будет передавать то, что @expenses.to_json возвращает, которое это просто куча json, пар ключ-значение. Как мне взять это и сделать стол?

Мой друг, который сделал много AJAX, но не руби на рельсах, сказал, что я могу просто написать HTML в функции успеха AJAX

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

Но это просто не похоже на путь Rails, чтобы поместить кучу кода Javascript, который просто помещает HTML в строку. И что тогда делает render: json, если я просто помещаю HTML в AJAX? Я действительно видел ответ здесь на переполнение стека, что had

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

Что было бы неплохо, так как у данных есть все json, которые я хочу. Но очевидно, что это не просто так работает.

Так что, если бы я мог получить некоторые разъяснения по всей этой путанице, если я даже приближаюсь к ней правильно с помощью JSON, или если бы я просто написал HTML в AJAX, это было бы очень ценно.

Правка 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-это то, что я уже использую в моем текущем коде таблицы. Однако, не будет отображаться: файл " expenses_table.формат html.erb " обновить всю страницу? Или как он будет знать, где сделать именно эту таблицу и заменить то, что было раньше? Это почти звучит легче ответить на format.js и сделать index.js.erb, потому что я знаю, что я мог бы заменить html с моим частичным. Но я не пробовал этого, так как все примеры, которые я видел, отвечают format.json. Если это звучит так, будто я просто запутался и не знаю, что происходит, то это потому, что я так и есть. Мне просто нужно кое-что прояснить. Спасибо за вашу помощь!

1   2   2014-10-03 21:27:31

1 ответ:

Если бы я вообще использовал json в AJAX.

Вы можете использовать любой тип строки, который вы хотите. Проблема заключается в разборе строки на стороне javascript для извлечения каждого фрагмента данных. Например, вы можете ответить строкой типа:

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

Затем ваш код javascript может разделить строку на ",", затем знак"=", затем использовать части для создания объекта, а затем вы можете использовать объект для ссылки на данные. С другой стороны, если данные передаются как JSON string, все, что вам нужно сделать в javascript, это:

var obj = JSON.parse(data);

...и obj будет чем-то похожим на рубиновый хэш, где вы можете искать ключи, чтобы получить значения.

Итак, я думаю, что мой основной вопрос заключается в том, как часть json works...My понимание заключается в том, что проходя @расходы, он в основном окажет то, что @expenses. to_json возвращает, что является просто кучей json, ключ-значение пары. Как мне взять это и сделать стол?

Это верно. Вы делаете стол используя свои навыки программирования.

Мой друг, который сделал много Аякса, но не руби на рельсах сказал, что я могу просто написать HTML в AJAX success function

Конечно, но хитрость заключается в том, чтобы программно взять данные в json и вставить их в таблицу html. Если вы хотите пойти этим путем, вы можете вернуть полную таблицу в качестве ответа, так что вы можете использовать ERB/Nokogiri/ruby на стороне сервера, чтобы вставить данные в таблицу. Тогда ты ... написал бы что-нибудь вроде:

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

Другой подход может заключаться в том, чтобы дать каждому <td> в таблице атрибут id, который равен ключу в ваших данных json. Затем вы можете использовать javascript для циклического перебора каждого ключа в данных json, поиска <td> с этим идентификатором, а затем заменить запись значением в данных json, соответствующим ключу.

Если вы хотите получить более конкретные предложения, вам придется опубликовать небольшой пример html вашей таблицы, а также то, как выглядит @expenses.to_json любить.

Кстати, функция jQuery ajax() имеет слишком много функций, поэтому все обычные запросы ajax имеют функции быстрого доступа, где соответствующие параметры заполняются для вас, например getJSON(), где вы просто указываете url, данные и функцию успеха. В этом случае это не сэкономит вам много работы, но, возможно, имя функции более описательно, чем "ajax".

Ответы на вопросы комментариев:

Я предполагаю, что мое замешательство тогда заключается в том, что JavaScript-это этот? Это оригинальный вызов AJAX?

Да. Это единственный javascript, который вам нужен для программирования.

Должно ли это быть в js.erb файл тогда для меня, чтобы вызвать какой-нибудь рубиновый код?

Нет. Вы бы использовали js.erb-файл, если вы хотите создать JS с помощью ruby (именно для этого и существует erb), и вы хотите вернуть JS-код в качестве ответа. Мы установили, что вы не хотите возвращать javascript в качестве ответа. Однако использование рельсов для настройки Аякс очень запутан, так что я, возможно, не понимаю сути ваших вопросов. Гораздо проще не использовать rails для генерации ajax javascript.

Насколько я понимаю, если вы помещаете remote: true в свой помощник rails html при создании html-формы, то при отправке формы запрос отправляется в приложение rails с заголовком Accept text/javascript. Это означает, что если у вашего действия есть блок respond_to со строкой format.js, эта строка будет выполняться, и вы можете отправить некоторый javascript вернитесь в браузер, например, в файл .js.erb, содержащий код, который выполняет ajax-запрос для получения данных о ваших расходах. Ваш браузер немедленно выполнит возвращенный js, который отправит запрос ajax обратно на сервер с запросом данных о расходах. Обратите внимание, что метод rails неэффективен: браузер должен отправить два запроса на сервер, чтобы выполнить один запрос ajax: один запрос отправляется на сервер, чтобы получить код js, который сделает запрос ajax для данных о расходах, а затем браузер выполняет 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 () будет искать частицу в каталоге app/views с именем shared/_table.html.erb.

Эти два файла, объединенные (вставленные в макет приложения), составляют индекс.формат html.вид на Эрб. Поскольку таблица теперь является отдельным шаблоном, только таблица может быть отрисована в ответ на запрос 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, и она отправляет запрос с заголовком Accept text/html в качестве наивысшего приоритета, поэтому форматируйте.html будет выбран в блоке respond_to. Edit: I первоначально написал действие индекса, думая, что будет формат.строка json для запроса ajax и формат.строка html для запроса без ajax. Но поскольку действие index теперь возвращает 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 в индекс.формат html.erb представление, которое должно дать понять, что javascript является частью html-страницы. Однако вы можете удалить javascript из индекса.формат html.Эрб и положил его в другом файле-но в конечном итоге js должен найти свой путь обратно в индекс.формат html.файл erb. Вот некоторые ссылки, которые обсуждают альтернативные места для javascript:

Почему рельсы не рендеринга .JS.файл erb?

Лучший способ добавить страницу конкретного javascript в приложение Rails 3?

Rails имеет что-то под названием asset pipeline, который является методом для запихивания всех javascript и css в как можно меньшее количество строк, удаляя все пробелы. Поскольку файлы меньше, ваш браузер может загружать их быстрее. Чтобы воспользоваться этимсокращением , вы должны поместить ваши assets, то есть ваши файлы javascript и css, в определенные каталоги. Это действительно не то, о чем вам нужно беспокоиться, потому что ваш код javascript не занимает тысячи страниц.