【问题标题】:Handle Rails flash messages after AJAX calls using ReactJS使用 ReactJS 在 AJAX 调用后处理 Rails 闪烁消息
【发布时间】:2015-01-13 21:49:35
【问题描述】:

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

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

所以我正在回答我自己的问题,即在下面使用 ReactJS 时如何在 AJAX 调用后呈现 Flash 消息,以回答可能遇到此问题的其他人并获得任何改进建议。

【问题讨论】:

    标签: javascript ruby-on-rails ajax reactjs


    【解决方案1】:

    此实现主要基于有关此主题的其他 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 array-of-2-element-arrays 数据结构将处理保持在最低限度,并允许直接从视图中使用组件,而不仅仅是 AJAX 调用。最后,本地 _flash_class 方法支持 Bootstrap 样式(当然可以根据需要调整为其他样式)。

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

    最后一部分是在页面加载时运行,因此取决于 JQuery 是否准备就绪。 React.render(正式名称为React.renderComponent)保存到一个全局变量中,以允许直接调用FlashMessages 对象的message 方法(使用的dummy 数组只是在第一次调用时播种一个空的flashes 数组——这可能也可以通过空测试在 React 类中处理)。每当触发ajaxComplete 时,就会调用handleFlashMessageHeader 函数并传递React 对象进行更新。

    非 AJAX
    因为 React 类假定原生数组数组,所以视图可以简单地称为

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

    而不是显示它,但是由于不支持动态响应,因此它当然用处不大。但是,无论以哪种方式调用它的能力都为某些用例提供了更大的灵活性。

    【讨论】:

      【解决方案2】:

      我无法得到 @rdnewman 的回答来使用 Rails 5 和 react-rails 1.9.0(对应于 React 15.3.0)。我认为 React 中的某些更改不允许外部访问函数,因此在 handleFlashMessagesHeader() 中对 .messages() 的调用不起作用(未找到函数)。

      我将答案改编为对我有用的东西。我只是在学习 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 目录中即可,一切正常。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-09
        • 1970-01-01
        • 1970-01-01
        • 2020-04-27
        • 1970-01-01
        • 2018-10-19
        相关资源
        最近更新 更多