Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

Ajax でも Rails の flash メッセージを表示したい!

More than 5 years have passed since last update.

概要

通常 Rails の flash メッセージは、アクションごとに HTML をレンダリングする場合にしか使用できません。
Rails 側の各アクションで flash メッセージの設定を行う必要があるからです。
つまり、Ajax 通信が終了 (complete) した際に flash メッセージを表示する手段は、デフォルトでは用意されていません。
一応、サーバ側で flash メッセージを表示するための JavaScript を用意して、それをレスポンスとして返すなどの方法はあります。

それでも Ajax 通信 (JSON リクエスト) でも flash メッセージを表示したいよぉ!
というわけでその方法を調べてみました。

方法

Rails 側ではレスポンスヘッダ ('X-' で始まるカスタムヘッダ) に flash メッセージを格納してレスポンスを返します。
そして、JavaScript 側ではそれを取り出すことで実現できました。

/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception

  after_action :flash_to_headers

  private

  def flash_to_headers
    return unless request.xhr?

    response.headers['X-Flash-Messages'] = flash_json

    # ページをリロードした際に flash メッセージが残ってしまうのを防ぐ。 
    flash.discard
  end

  def flash_json
    flash.inject({}) do |hash, (type, message)|
      # XSS 対策を施す。
      message = "#{ERB::Util.html_escape(message)}"
      # 日本語のメッセージをレスポンスヘッダに含めるために URL エンコードしておく。
      message = URI.escape(message)
      hash[type] = message
      hash
    end.to_json
  end
end
/app/controllers/magicas_controller.rb
class MagicasController < ApplicationController
  respond_to :json, only: [:update]

  def update
    @magica = Magica.find(params[:id])
    @magica.update!(update_params)

    flash[:notice]  = '更新しました!'
    flash[:warning] = '更新しました?'
    flash[:error]   = '更新できず…orz'

    respond_with @magica
  end
end
/app/views/layouts/application.html.slim
doctype html
html lang="ja"
  head
    meta name="viewport" content="width=device-width, initial-scale=1.0"
    title Magica System
    - description_content = content_for?(:description) ? yield(:description) : "Bootstrap App"
    meta name="description" content=description_content

  = stylesheet_link_tag    "application", media: 'all', 'data-turbolinks-track' => true
  = javascript_include_tag "application", 'data-turbolinks-track' => true
  = csrf_meta_tags
  = yield(:head)

  body class=body_class
    = render 'layouts/navigation'
    .js-main-content.container
      / ここに flash メッセージを表示する。
      .js-flash 
        = render 'layouts/messages'
      = yield
/app/assets/javascripts/flash.js.coffee
# Underscore.js (あるいは Lo-Dash) を導入していることを前提とする。
$(document).ajaxComplete (event, xhr, settings) ->
  flash = $.parseJSON(xhr.getResponseHeader('X-Flash-Messages'))
  return if _.isEmpty(flash)

  $flash = $('.js-flash')
  $flash.empty()

  index = 1
  _.each flash, (message, type) ->

    alertType =
      switch type
        when 'notice'
          'success'
        when 'warning'
          'warning'
        when 'error'
          'danger'
        else
          'info'

    alertId = "js-alert-#{index}"
    decodedMessage = decodeURIComponent(message)

    $flash.append """
      <div class='alert alert-#{alertType}' data-alert-id='#{alertId}'>
        <a class='close' data-target='[data-alert-id=#{alertId}]' data-dismiss='alert'>&times;</a>
        <div>#{decodedMessage}</div>
      </i>
    """

    index++

  return

実行例

magica_system.png

でた! ✌ ('ω' ✌ )三 ✌ ('ω') ✌ 三( ✌ 'ω') ✌

参考

QUANON
あんた、マジなんだな?
http://quanon.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away