使用ReactJS调用AJAX后处理Rails flash消息

时间:2014-11-17 03:17:27

标签: javascript ruby-on-rails ajax reactjs

有几篇帖子描述了在AJAX调用后显示flash消息(例如,Rails doesn't display flash messages after ajax callHow do you handle Rail's flash with Ajax requests?)。

我正在使用ReactJSreact-rails并希望这样做,并希望利用React的动态显示渲染。

所以我在回答我自己的问题时,如果在AJAX调用之后如何使用下面的ReactJS来回复flash消息,以回答可能遇到此问题的其他人并获得改进建议。

3 个答案:

答案 0 :(得分:6)

此实施主要基于此主题的其他SO帖子(请参阅相关参考文献)。该代码假定Rails 4.x包含资产管道,HAML用于视图,Bootstrap用于样式,以及使用react-rails gem。

更新:可以在my blog找到稍长时间的讨论。)

<强>布局
这是app/views/layouts/application.html.haml文件:

!!!
%html
  %head
    %title
      Demo

    = stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true
    = javascript_include_tag 'application', 'data-turbolinks-track' => true
    = csrf_meta_tags

  %body
    %div.container
      %div#flash_messages
      = yield

请注意,div的ID为#flash_messages。此容器div的位置决定了Flash消息的显示位置。

<强>控制器
下面的私有例程只是将本机数组的使用转换为标头中的可解析JSON。

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  after_filter :flash_to_http_header

private
  def flash_to_http_header
    return unless request.xhr?
    return if flash.empty?
    response.headers['X-FlashMessages'] = flash.to_hash.to_json
    flash.discard  # don't want the flash to appear when you reload page
  end
end

Javascript(ReactJS)
以下javascript由React类,用于处理响应头中的flash消息的全局函数以及用于完成AJAX调用的JQuery处理程序组成。它被放入一个JX文件(我用app/assets/javascripts/react/flash_messages.js.jsx)来保持相关的JS功能。我将在下面进行更多讨论。

/** @jsx React.DOM */

var FlashMessages = React.createClass({
  getInitialState: function() {
    return {messages: this.props.messages};
  },

  messages: function (messageArray) {
    this.replaceState({messages: messageArray});
  },

  render: function() {
    return (
      <div className='flash_messages_component'>
        {this.state.messages.map(function(message, index) {
          _level = message[0];
          _text  = message[1];
          return (
            <div key={index} className={this._flash_class(_level)}>
              {_text}
            </div>
          );
        }.bind(this))}
      </div>
    )
  },

  _flash_class: function(level) {
    var _result = 'alert alert-error';
    if (level === 'notice') {
      _result = 'alert alert-info';
    } else if (level === 'success') {
      _result = 'alert alert-success';
    } else if (level === 'error') {
      _result = 'alert alert-error';
    } else if (level === 'alert') {
      _result = 'alert alert-error';
    }
    return _result;
  }

});

function handleFlashMessagesHeader(node, xhr) {
  var _message_array = new Array();
  var _raw_messages = xhr.getResponseHeader("X-FlashMessages")
  if (_raw_messages) {
    var _json_messages = JSON.parse(_raw_messages);
    count = 0
    for (var key in _json_messages) {
      _message_array[count] = new Array();
      _message_array[count][0] = key;
      _message_array[count][1] = _json_messages[key];
      count += 1;
    }
  }
  node.messages(_message_array);
}

$(document).ready(function() {
  var dummy = new Array();
  var flashDiv = React.render(<FlashMessages messages={dummy} />, $('#flash_messages')[0]);

  $(document).ajaxComplete(function(event, xhr, settings) {
    handleFlashMessagesHeader(flashDiv, xhr);
  });
});

FlashMessages React类做了一些事情。首先,它将道具移动到状态。通常这将是一个反模式,但这样做可以使非React代码在需要时触发更改。 messages函数是触发器,意味着由外部JS代码调用。 render的主要处理假设Rails flash-native-of-2-element-arrays数据结构将处理保持在最小,并允许直接从视图而不是AJAX调用使用该组件。最后,本地_flash_class方法支持Bootstrap样式(当然可以根据需要调整其他样式)。

handleFlashMessagesHeader是一个全局函数,用于将JSON转换回由Rails控制器过滤器方法完成的数组数组。请注意,它使用带有id标记的DOM元素来自Rails应用程序视图/布局。

最后一节是为了在页面加载时运行,所以取决于JQuery就绪。 React.render(正式React.renderComponent)保存到全局变量,以允许直接调用FlashMessages对象的message方法(dummy数组used只是在第一次调用时播种空闪烁数组 - 这可能也可以通过null测试在React类中处理。每当触发ajaxComplete时,都会调用handleFlashMessageHeader函数并传递React对象进行更新。

<强>非AJAX
因为React类假定为本机数组,所以视图可以简单地调用

= react-component 'FlashMessages', flash, :div

而不是显示它,但当然它的使用较少,因为不支持动态响应。但是,以某种方式调用它的能力为某些用例提供了更大的灵活性。

答案 1 :(得分:1)

我无法得到@ rdnewman使用Rails 5和react-rails 1.9.0(对应于React 15.3.0)的答案。我认为React中的某些内容发生了变化,不允许外部访问函数,因此.messages()中对handleFlashMessagesHeader()的调用不起作用(找不到函数)。

我将答案改编为对我有用的东西。我刚学习React,所以可能有更好的方法,但这就是我所拥有的:

app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  after_action :flash_to_http_header, if: -> { request.xhr? }

  private

  def flash_to_http_header
    return if flash.empty?
    response.headers['X-FlashMessages'] = flash.to_hash.to_json
    flash.discard
  end
end

app/assets/javascripts/components/flash_messages.js.coffee

@FlashMessages = React.createClass
  getInitialState: ->
    messages: @props.data
  getDefaultProps: ->
    messages: {}
  componentDidMount: ->
    $(document).ajaxComplete ((event, xhr, settings) ->
      header_messages = xhr.getResponseHeader('X-FlashMessages')
      messages = {}
      if header_messages
        messages = JSON.parse(header_messages)
      @replaceState {messages: messages}
    ).bind(this)
  messageClass: (messageType) ->
    if messageType == 'notice'
      return 'alert alert-warning'
    if messageType == 'success'
      return 'alert alert-success'
    return 'alert alert-error'
  render: ->
    React.DOM.div
      className: 'flash-messages'
      for messageType of @state.messages
        React.DOM.div
          className: @messageClass(messageType)
          key: messageType
          @state.messages[messageType]

app/views/layouts/application.html.erb

<%= react_component 'FlashMessages', { data: flash.to_hash } %>

这将在页面加载时呈现标准Flash消息,并在任何Ajax请求之后更新/替换它们。如果你正在使用react-rails gem,你可以将文件放到components目录中,一切都应该有效。

答案 2 :(得分:1)

查看此博文Displaying Rails flash messages with React,其中描述了您的要求:

enter image description here

简而言之,它展示了如何创建 FlashMessages 反应组件并保存对它的引用:

window.flash_messages = this; // in FlashMessages constructor 

因此可以在客户端发送消息:

window.flash_messages.addMessage({ id: 'id', text: 'Hello, fellas!', type: 'success' });