Rails не отображает флеш-сообщения после вызова ajax

Я пишу приложение rails 4.0.2 и пытаюсь получить уведомление Flash, отображаемое в моем представлении после события AJAX.

На мой взгляд, я отображаю календарь с днями, на которые пользователь может щелкнуть. Когда они это делают, я запускаю событие AJAX через обработчик события onclick, который обновляет мою модель, добавляя или удаляя запись. После запуска события я завершаю обновление страницы, чтобы отобразить обновленные результаты.

Я обнаружил, что должен был обновить страницу во время события нажатия JS, чтобы правильно обновить представление. Перенаправления или рендеринга в одном контроллере было недостаточно.

Итак, для этого я установил уведомление Flash в моем контроллере...

def set_conflicts
  @conflict = @member.conflicts.for_date(params[:date]).first

  if @conflict
    @conflict.destroy
  else
    conflict_date = Date.parse(params[:date])
    unless Conflict.create(
        month: conflict_date.month,
        day:   conflict_date.day,
        year:  conflict_date.year,
        member: @member
    )
      flash[:alert] = 'Oops, there was a problem updating conflicts'
    end
  end
  flash[:notice] = 'This is a test!'
  redirect_to manage_member_conflicts_path(@member)
end

... и включил следующую логику отображения флэш-памяти в моем приложении. Haml...

...
%body
  = p flash.inspect
  - flash.each do |type, message|
    = content_tag(:div, message, :class => "flash-#{type}")

  = render 'devise/menu/login_items'
  = yield

Примечание: я использую HAML вместо ERB в моих представлениях

Тем не менее, независимо от того, что я пытаюсь, Flash-сообщение не отображается. Все остальное работает как положено, кроме флеш-сообщения, и я не смог понять, почему.

Я подозреваю, что это как-то связано с обновлением AJAX, которое я делаю в сочетании с перенаправлением (и, возможно, даже с турболинками), но я просмотрел другие ответы здесь на SO и просто не могу заставить его работать. Очевидно, я что-то упустил.

Вот обработчик кликов JS (это довольно просто):

window.calendarClick = (eventDate, linkURL) ->
  event = $(document.getElementById(eventDate))
  event.toggleClass('selected')

  # Set or Remove conflicts
  $.ajax
    url: linkURL
    data:
      date: eventDate

  # Refresh the page
  $.ajax
    url: '',
    context: document.body,
    success: (s,x) ->
      $(this).html(s)

3 ответа

Решение

Во-первых, спасибо за исчерпывающий вопрос

Вот такой же исчерпывающий ответ: как вы обрабатываете флешку Rail с Ajax-запросами?


Ajax

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

Почему бы вам не попробовать использовать .js.erb файл в views Папка, для обработки JS напрямую методом:

def set_conflicts
  @conflict = @member.conflicts.for_date(params[:date]).first

  if @conflict
    @conflict.destroy
  else
    conflict_date = Date.parse(params[:date])
    unless Conflict.create(
        month: conflict_date.month,
        day:   conflict_date.day,
        year:  conflict_date.year,
        member: @member
    )
      flash[:alert] = 'Oops, there was a problem updating conflicts'
    end
  end
  flash[:notice] = 'This is a test!'

  respond_to do |format|
      format.html { redirect_to manage_member_conflicts_path(@member) }
      format.js 
  end
end

#app/views/controller/set_conflicts.js.erb
$("#your_element").html(<%=j render manage_member_conflicts_path(@member) ) %>);

отклик

Хотя я не знаю этого наверняка, я знаю, что объекты Flash обрабатываются промежуточным программным обеспечением ActionDispatch::Flash. Я считаю, что это не обрабатывается для XHR так же, как стандартные HTTP-запросы, следовательно, предотвращает показ Flash в вашем ответе

Это означает, что вам нужно будет что-то сделать, чтобы передать объект Flash через ваш запрос Ajax. И в соответствии с вопросом, который я разместил в верхней части этого ответа, вы можете использовать response headers сделать это:

class ApplicationController < ActionController::Base
after_filter :flash_to_headers

def flash_to_headers
  return unless request.xhr?
  response.headers['X-Message'] = flash[:error]  unless flash[:error].blank?
  # repeat for other flash types...

  flash.discard  # don't want the flash to appear when you reload page
end

$(document).ajaxError(function(event, request) {
  var msg = request.getResponseHeader('X-Message');
  if (msg) alert(msg);
});

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

После нескольких часов поиска я понял, что уже делал то, что нужно было сделать, пропустив следующие строки в моем контроллере:

flash[:error] = "My flash message error."

respond_to do |format|
  format.js 
end

Flash [: error] устанавливает Flash-сообщение для следующего представления, response_to понимает, что у меня удаленный: true, установленный в моем link_to (оригинальный запрос ajax):

<%= link_to "My Ajax Request", some_path(@some_value), remote: true %>

и отвечает на ссылку как JavaScript. Вы можете включить format.html в этот блок, если вы все еще хотите иметь возможность вызывать тот же контроллер без удаленного значения true.

В моем application.html.erb у меня есть партиал, который отображает флеш-сообщения:

<%= render "layouts/flash_notices" %>

и эта часть содержит div, который выглядит следующим образом:

<div id="flash_messages">
  <% flash.each do |type, message| %>
    <p><%= type %>: <%= message %></p>
  <% end %>
</div>

Наконец, я создал представление с именем MY_CONTROLLER_ACTION_NAME.js.erb и вставил:

<% flash.each do |type, message| %>
  $("#flash_messages").html("<%= type.to_s.humanize %>: <%= message.html_safe %>")
<% end %>

Теперь, когда я щелкаю ссылку с поддержкой AJAX, выполняется действие контроллера, устанавливается флэш-сообщение, контроллер загружает представление js.erb и, наконец, выполняется JavaScript, чтобы заменить содержимое моего div на id="flash_messages". Мой исходный вид обновляется с помощью флэш-сообщения, и с миром все в порядке.

Примечание. Если вы используете Bootstrap или какую-то другую специальную CSS-среду, вы можете включить этот код в вызов $("#flash_messages'). Html(" ") файла js.erb. Я включил код Bootstrap следующим образом:

$("#flash_messages').html("<div class='alert <%= bootstrap_class_for(type) %> alert-dismissible' role='alert'><button class='close' data-dismiss='alert'>×</button><%= message.html_safe %></div>")

Я знаю, что этот вопрос был опубликован несколько месяцев назад, но при поиске именно этой проблемы всегда кажется, что вам нужно отправить флэш-сообщения через заголовки. Я хотел показать, что есть другой метод. Надеюсь, это поможет моим коллегам по Rails новичкам будущего!

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

@Celsian, спасибо, это было очень полезно. С использованием flash[:error]однако означает, что вспышка будет задерживаться после обновления страницы. flash.now[:error] = "My flash message error."решает это. Покрывая также ситуацию без AJAX, я пошел с:

        respond_to do |format|
     format.html { flash[:error] = "error"
                   redirect_to request.referrer }
     format.js { flash.now[:error] = "error" }
   end

таким образом, вспышка прибывает (и не задерживается) независимо от того, является ли запрос AJAX или нет.

Я также заметил небольшую опечатку в вашей расширенной загрузочной строке кода:

      $("#flash_messages')

должно быть

      $('#flash_messages')
Другие вопросы по тегам