有几篇帖子描述了在AJAX调用后显示flash消息(例如,Rails doesn't display flash messages after ajax call和How do you handle Rail's flash with Ajax requests?)。
我正在使用ReactJS与react-rails并希望这样做,并希望利用React的动态显示渲染。
所以我在回答我自己的问题时,如果在AJAX调用之后如何使用下面的ReactJS来回复flash消息,以回答可能遇到此问题的其他人并获得改进建议。
答案 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,其中描述了您的要求:
简而言之,它展示了如何创建 FlashMessages 反应组件并保存对它的引用:
window.flash_messages = this; // in FlashMessages constructor
因此可以在客户端发送消息:
window.flash_messages.addMessage({ id: 'id', text: 'Hello, fellas!', type: 'success' });