Як ви обробляєте спалах Rail із запитами Ajax?


77

Я дуже задоволений рішенням, яке я придумав. В основному, у мене є допоміжний метод, який перезавантажує вбудований спалах, а потім у мене є after_filter, який очищає спалах, якщо запит xhr. Хтось має простіше рішення, ніж це?

Оновлення: Наведене вище рішення було написано ще в Rails 1.x і більше не підтримується.


3
Мені подобається ваше рішення ..after_filter { flash.discard if request.xhr? }
Пітер Ерліх,

Відповіді:


64

Ви також можете зберігати флеш-повідомлення в заголовках відповідей за допомогою блоку after_filter і відображати їх за допомогою javascript:

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

А в application.js додайте глобальний обробник ajax. Для jquery зробіть щось подібне:

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

Замініть alert () на власну функцію flash flash або спробуйте jGrowl.


6
Крім того, ви можете зберегти тип повідомлення: response.headers['X-Message-Type'] = flash_type(тоді як flash_type повертає найважливіший тип (помилка> успіх> повідомлення). Крім того, ви можете використовувати ajaxCompleteпотім для включення випадків успіху:$(document).ajaxComplete(function(e, request, opts) { fireFlash(request.getResponseHeader('X-Message'), request.getResponseHeader('X-Message-Type')); });
хрусткий

Якщо ваша програма rails використовує прототип для подання запиту ajax, попередній обробник jquery не буде працювати. Вам потрібно буде використовувати відповідні обробники прототипів. Дивіться мою відповідь вище.
emzero

1
чому в коментарях у верхній частині application.js сказано, що не бажано додавати туди код?
Стів

29

І ось моя версія на основі @emzero, із модифікаціями для роботи з jQuery, протестована на Rails 3.2

application_controller.rb

class ApplicationController < ActionController::Base
    protect_from_forgery

    after_filter :flash_to_headers

    def flash_to_headers
        return unless request.xhr?
        response.headers['X-Message'] = flash_message
        response.headers["X-Message-Type"] = flash_type.to_s

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

    private

    def flash_message
        [:error, :warning, :notice].each do |type|
            return flash[type] unless flash[type].blank?
        end
    end

    def flash_type
        [:error, :warning, :notice].each do |type|
            return type unless flash[type].blank?
        end
    end
end

application.js

// FLASH NOTICE ANIMATION
var fade_flash = function() {
    $("#flash_notice").delay(5000).fadeOut("slow");
    $("#flash_alert").delay(5000).fadeOut("slow");
    $("#flash_error").delay(5000).fadeOut("slow");
};
fade_flash();

var show_ajax_message = function(msg, type) {
    $("#flash-message").html('<div id="flash_'+type+'">'+msg+'</div>');
    fade_flash();
};

$(document).ajaxComplete(function(event, request) {
    var msg = request.getResponseHeader('X-Message');
    var type = request.getResponseHeader('X-Message-Type');
    show_ajax_message(msg, type); //use whatever popup, notification or whatever plugin you want
});

макет: application.html.haml

        #flash-message
            - flash.each do |name, msg|
                = content_tag :div, msg, :id => "flash_#{name}"

Це також працює на рейках 3.1.0. Дякую Вікторе, працював прямо нестандартно для мене.
LearningRoR

2
Чудова компіляція, хоча не повинна "$ (" # flash-message "). AjaxComplete (функція (подія, запит)" бути "$ (документ)"?
nullnullnull

З якоїсь причини, "хіба що flash [type] .blank?" не працює належним чином у деяких ситуаціях. Дія без спалахів призведе до виразу "помилка, попередження, повідомлення". Я виправив це в js, використовуючи рядок "if (msg! =" Error, warning, note ") show_ajax_message (msg, type) ', але це, очевидно, хакерське рішення. Однак я не можу зрозуміти справжню причину проблеми.
nullnullnull

1
[:error, :warning, :notice].each {}повертає масив, якщо умова повернення не виконується, тому цей код потребує трохи коригування .. але в іншому випадку корисно.
radixhound

3
Якщо перероблено код, щоб не повернути рядок "помилка, попередження, зауваження", а також гарно пограти з twitter-bootstrap gist.github.com/hbrandl/5253211
Хартвіг,

15

Це потрібно у відповіді js

Якщо ви використовуєте RSJ:

page.replace_html :notice, flash[:notice]
flash.discard

Якщо ви використовуєте jQuery:

$("#flash_notice").html(<%=escape_javascript(flash.delete(:notice)) %>');

3
Схоже, у Rails 3.1+ вам потрібно flash.discard(:notice)скоріше скористатися , ніжflash.delete(:notice)
Адріан Макнейл

15

Я зробив це таким чином ..

контролер :

respond_to do |format|
    flash.now[:notice] = @msg / 'blah blah...'
    format.html 
    format.js
  end

вид:

<div id='notice'>
    <%= render :partial => 'layouts/flash' , :locals => { :flash => flash } %>
</div>        

макети / _flash.html.erb

<% flash.each do |name, msg| %>
            <div class="alert-message info"> 
                <a class="close dismiss" href="#">x</a> 
                <p><%= msg %></p>
            </div>
<% end %>

post.js.erb

$("#notice").html("<%= escape_javascript(render :partial => 'layouts/flash' , :locals => { :flash => flash }).html_safe %>");

У вашому контролері я бачу друкарську помилку. flash.now[:notice]= @msg / 'blah blah..'Мені також було цікаво, ви б помістили post.js.erb у шлях app / assets / javascripts, правда?
RajG

1
post.js.erb переходить у папку подання контролерів, у цьому випадку це "views / posts / post.js.erb". msg / "бла-бла" я мав на увазі повідомлення чи якесь випадкове повідомлення на зразок "бла-бла"
dbKooper

10

Будуючи поверх інших -

(Ми передаємо повний флеш-об'єкт як JSON, що дозволяє нам реконструювати повний флеш-об'єкт у браузері. Це може бути використано для забезпечення відображення всіх флеш-повідомлень на випадок, якщо Rails генерує кілька флеш-повідомлень.)

#application_controller.rb
class ApplicationController < ActionController::Base
  after_filter :flash_to_headers

  def flash_to_headers
    if request.xhr?
      #avoiding XSS injections via flash
      flash_json = Hash[flash.map{|k,v| [k,ERB::Util.h(v)] }].to_json
      response.headers['X-Flash-Messages'] = flash_json
      flash.discard
    end
  end
end
//application.js
$(document).ajaxComplete(function(event, request){
  var flash = $.parseJSON(request.getResponseHeader('X-Flash-Messages'));
  if(!flash) return;
  if(flash.notice) { /* code to display the 'notice' flash */ $('.flash.notice').html(flash.notice); }
  if(flash.error) { /* code to display the 'error' flash */ alert(flash.error); }
  //so forth
}

Thnx. Я змінив функцію в application.js, щоб вона сподобалася, $.each( $.parseJSON(request.getResponseHeader('X-Flash-Messages')), function(key,value){ flash_message(key,value) });якщо у вас є функція js, яка відображає флеш-повідомлення з типом спалаху та повідомленням params
Rahul garg

6

Схоже, що вам потрібно flash.now[:notice], що доступно лише в поточній дії, а не в наступній. Ви можете поглянути на документацію тут: http://api.rubyonrails.com/classes/ActionController/Flash/FlashHash.html#M000327


Гей, мені це подобається! Ніхто інший ? : S
Бачет

1
@le_Daf: Використання flash.now- це рішення іншої проблеми. Зміст flash.nowне чарівно вставляється на поточну сторінку через зворотний виклик Ajax.
Плутанина

3
Мав би називатися flash.eventually замість flash.now;)
Дебора

5

Призначте повідомлення в контролері так:

  flash.now[:notice] = 'Your message'

app / views / layout / application.js.erb - Макет для запитів Ajax. Тут ви можете просто використовувати

  <%= yield %>
  alert('<%= escape_javascript(flash.now[:notice]) %>'); 

або з деякою насиченою анімацією за допомогою гриттера: http://boedesign.com/demos/gritter/

  <%= yield %>
  <% if flash.now[:notice] %>
    $.gritter.add({
      title: '--',
      text: '<%= escape_javascript(flash.now[:notice]) %>'
    });
  <% end %>

3

На основі відповіді gudleik:

class ApplicationController < ActionController::Base
  after_filter :flash_to_headers

def flash_to_headers
  return unless request.xhr?
  response.headers['X-Message'] = flash_message
  response.headers["X-Message-Type"] = flash_type

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

private

def flash_message
  [:error, :warning, :notice].each do |type|
    return flash[type] unless flash[type].blank?
  end
end

def flash_type
  [:error, :warning, :notice].each do |type|
    return type unless flash[type].blank?
  end
end

Потім у своєму application.js (якщо ви використовуєте власні помічники прототипу Rails) додайте:

Ajax.Responders.register({
onComplete: function(event, request) {
   var msg = request.getResponseHeader('X-Message');
   var type = request.getResponseHeader('X-Message-Type');
   showAjaxMessage(msg, type); //use whatever popup, notification or whatever plugin you want
   }
});

ти можеш пояснити return unless request.xhr? Я маю на увазі в кінці поточного запиту, якщо ми; ми додали будь-які сповіщення про флеш, ми додаємо їх до заголовків відповідей, тоді в js ми їх читаємо - круто, але я не зовсім впевнений, чому у нас вищезазначений рядок
Майкл К Медісон

3

Існує самоцвіт « Ненав’язливий спалах», який автоматично кодує флеш-повідомлення у файлі cookie. Яваскріпт на кінці клієнта перевіряє наявність флеш-пам'яті та відображає його як завгодно. Це працює як у звичайних запитах, так і в запитах ajax.


Вирішив для мене всі питання.
WM

3

Я модифікував відповідь Віктора С, щоб виправити деякі випадки, коли flash[type].blank?не спрацювали, як зазначало мало людей у ​​коментарях.

after_filter :flash_to_headers

def flash_to_headers
   return unless request.xhr?
   response.headers['X-Message'] = flash_message
   response.headers["X-Message-Type"] = flash_type.to_s

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

private

def flash_message
   [:error, :warning, :notice, nil].each do |type|
     return "" if type.nil?
     return flash[type] unless flash[type].blank?
   end
end

def flash_type
   [:error, :warning, :notice, nil].each do |type|
       return "" if type.nil?
       return type unless flash[type].blank?
   end
end

Тоді відпочинок той самий

// FLASH NOTICE ANIMATION

var fade_flash = function() {
    $(".flash_notice").delay(5000).fadeOut("slow");
    $(".flash_alert").delay(5000).fadeOut("slow");
    $(".flash_error").delay(5000).fadeOut("slow");
};

var show_ajax_message = function(msg, type) {
    $(".flash_message").html('<div class="flash_'+type+'">'+msg+'</div>');
    fade_flash();
};

$( document ).ajaxComplete(function(event, request) {
    var msg = request.getResponseHeader('X-Message');
    var type = request.getResponseHeader('X-Message-Type');
    show_ajax_message(msg, type); //use whatever popup, notification or whatever plugin you want

});

3

Ось моя версія (робота з кількома сповіщеннями про спалах та спеціальними символами кодування UTF-8):

Всередині контролера додатків:

after_filter :flash_to_headers
def flash_to_headers
  return unless request.xhr?
  [:error, :warning, :notice].each do |type|
    if flash[type]
      response.headers["X-Ajax-#{type.to_s.humanize}"] = flash[type]
    end
  end
  flash.discard
end

Всередині мого кавового сценарію (версія завантажувального Twitter):

css_class = {
    Notice: 'success',
    Warning: 'warning',
    Error: 'error'
}
$(document).ajaxComplete (event, request) ->
  for type in ["Notice", "Warning", "Error"]
    msg = request.getResponseHeader("X-Ajax-#{type}")
    if msg?
      $('#notices').append("<div class=\"alert #{css_class[type]}\">#{decodeURIComponent(escape(msg))}</div>")

Він повинен працювати з азіатськими символами з розшифровкою utf8 ;-)
Люк Буассай

1
ні це не так!. Я перевірив документ RFC і виявив, що в заголовку http підтримуються лише символи ASCII.
fengd

1

Іншим способом було б оновити / відобразити div "повідомлення" із повідомленням від обробника ваших запитів Ajax "OnFailure". Це дає вам можливість показувати ці флеш-повідомлення з необхідним ефектом. Я цим користувався

 render: text => "Сталася якась помилка",: status => 444

у Javascript

 новий AjaxRequest (...

  ,
   OnFailure: функція (транспорт) {
      $ ("# повідомлення"). оновлення (transport.responseText);
     // показати повідомлення  
   }

);

HTH



0

Єдине вдосконалення, про яке я можу подумати, - це зробити за замовчуванням page.reload_flash (не потрібно розміщувати його на кожному файлі rjs і робити його швидким, якщо ви не хочете перезавантажувати флеш, щось на зразок page.keep_flash.

Я б не знав, з чого почати, але знаючи деякі рейки, я впевнений, що це не так складно.


0

Якщо ви хочете використовувати виклики AJAX, redirect_to не слід використовувати в контролері. Швидше, флеш-повідомлення слід чітко позначати:

У вашому_контролері:

respond_to :js

def your_ajax_method
  flash[:notice] = 'Your message!'
end

У поданні, яке іменовано вашим_аякс_методом_в_контролері

your_ajax_method_in_the_controller.js.haml

:plain
  $("form[data-remote]")
    .on("ajax:success", function(e, data, status, xhr) {
      $('.messages').html("#{escape_javascript(render 'layouts/messages')}");
      setTimeout(function(){ $(".alert").alert('close') }, 5000);
    })

Зверніть увагу, що клас повідомлень є опорною точкою для відтворення повідомлень. Цей клас повинен бути присутнім у вашому поданні або макеті програми. Якщо ви використовуєте ERB, рядок стає$('.messages').html("<%= j(render 'layouts/messages') %>");

Вищезазначений JavaScript, вбудований у HAML / ERB, є ключем до відображення флеш-повідомлень під час використання AJAX. Усі інші компоненти залишаються незмінними для дзвінків, що не стосуються AJAX.

Ви можете використовувати your_ajax_method_in_the_controller.js.coffeeзвичайний .js, але тоді змінні rails будуть недоступні для JS / Coffee. Незважаючи на те, що я не використовую тут змінні, я волію обертати JS у HAML, щоб зберегти послідовну базу коду.

Я використовую Twitter Bootstrap для стилізації повідомлень, тим самим $(".alert").alert('close')згасаючи повідомлення. І ось повідомлення часткові:

макети / _messages.html.haml

- flash.each do |name, msg|
  - if msg.is_a?(String)
    .alert-messages
      %div{class: "alert alert-#{name == :notice ? "success" : "error"} fade in"}
        %a.close{"data-dismiss" => "alert"} 
          %i.icon-remove-circle
        = content_tag :div, msg, id: "flash_#{name}"

Про всяк випадок CSS для попереджень знаходиться нижче

.alert-messages {
  position: fixed;
  top: 37px;
  left: 30%;
  right: 30%;
  z-index: 7000;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.