如何在交互模式下添加多个flash[:notice]并显示全部flash通知?
针对你的两个需求,我分两种场景给你具体解决方案,不管你要不要继续用unobtrusive_flash都能搞定:
一、显示全部Flash通知(非实时场景)
如果你想继续用unobtrusive_flash,默认它会用新通知替换容器里的旧内容,所以要改两个地方:
修改视图,确保能渲染所有历史消息
把原来的空容器改成手动遍历所有Flash消息的结构,这样页面加载时就能显示之前的所有通知:<div class="unobtrusive-flash-container"> <% flash.each do |type, messages| %> <% Array(messages).each do |message| %> <div class="flash <%= type %>"><%= message %></div> <% end %> <% end %> </div>这里用
Array(messages)是为了兼容单个字符串和数组两种情况,避免遍历出错。调整
unobtrusive_flash的JS配置,让新通知追加而不是替换
在你的JavaScript文件里添加这行配置,这样新的通知会加到容器末尾,不会覆盖旧的:UnobtrusiveFlash.flashOptions.append = true;
如果不想用unobtrusive_flash,直接用上面的视图代码就行,每次请求都会渲染所有Flash消息,完全不需要依赖这个gem。
二、应用运行时交互式显示实时通知
你的代码里有Twitter流式处理逻辑,这是服务器端的长连接操作,普通的Flash是基于HTTP请求周期的,没法实时推送到客户端。要实现交互式实时显示,得用WebSocket,Rails自带的Action Cable是最佳选择:
步骤1:创建Action Cable频道
先生成一个处理通知的频道:
rails generate channel Notification
步骤2:配置频道允许订阅
修改app/channels/notification_channel.rb,设置要推送的流(如果没有用户系统,用全局流就行):
class NotificationChannel < ApplicationCable::Channel def subscribed # 全局推送所有用户都能看到,或者按用户ID区分:"notification_#{current_user.id}" stream_from "notifications" end def unsubscribed # 取消订阅时的清理逻辑(可选) end end
步骤3:前端接收并显示通知
修改app/javascript/channels/notification_channel.js,收到服务器推送的消息后,把通知加到页面上:
import consumer from "./consumer" consumer.subscriptions.create("NotificationChannel", { connected() { console.log('已连接到通知频道'); }, disconnected() { console.log('与通知频道断开连接'); }, received(data) { // 创建通知元素并添加到容器 const container = document.querySelector('.flash-container'); const flashDiv = document.createElement('div'); flashDiv.className = `flash ${data.type}`; flashDiv.textContent = data.message; container.appendChild(flashDiv); // 可选:让通知3秒后自动消失 setTimeout(() => flashDiv.remove(), 3000); } });
步骤4:把Twitter流移到后台任务并广播通知
控制器里不能长时间阻塞请求,所以把Twitter流逻辑放到Active Job里:
# app/jobs/twitter_stream_job.rb class TwitterStreamJob < ApplicationJob queue_as :default def perform config = { consumer_key: '******', consumer_secret: '*******', access_token: '********', access_token_secret: '********' } rClient = Twitter::REST::Client.new config sClient = Twitter::Streaming::Client.new(config) topics = ['#trump', '#rails'] sClient.filter(track: topics.join(',')) do |tweet| if tweet.is_a?(Twitter::Tweet) puts tweet.text # 广播通知到所有订阅的客户端 ActionCable.server.broadcast 'notifications', { type: 'notice', message: tweet.text } rClient.retweet tweet end end rescue => e puts 'error occurred, waiting for 5 seconds' # 广播错误通知 ActionCable.server.broadcast 'notifications', { type: 'error', message: 'error occurred, waiting for 5 seconds' } sleep 5 retry # 出错后重试连接 end end
然后在控制器里启动这个任务:
def start_stream TwitterStreamJob.perform_later redirect_to root_path, notice: 'Twitter流已启动' end
步骤5:添加通知容器到视图
把原来的unobtrusive-flash-container换成(或者新增)这个容器:
<div class="flash-container"></div>
这样,当Twitter流收到新推文或者出现错误时,服务器会实时把通知推送到所有在线客户端,页面上会自动显示新通知,完全是交互式的!
内容的提问来源于stack exchange,提问作者Jelle

