在Shiny模块中集成JavaScript:趋势线不显示问题排查
问题解决:Shiny应用中趋势线无法显示(已获取客户端时区)
问题背景
开发了一个可生成时间序列数据趋势线的Shiny应用,通过JavaScript代码获取客户端时区,用于将时序数据转换为本地时区。目前浏览器控制台能正常输出时区信息,但趋势线无法显示。
问题根源分析
- 未使用模块命名空间传递时区参数:模块内的JavaScript代码直接设置了
client_timezone输入值,但模块的输入需要带有命名空间前缀,导致服务器端无法接收到时区数据,req(timezone)一直不满足,Plotly图表无法渲染。 - 重复执行时区获取逻辑:无触发条件的
observe会反复执行JavaScript代码,造成不必要的资源消耗。 - 未实现时区转换逻辑:原代码没有将UTC时间转换为客户端本地时区,未完成核心需求。
修复方案
1. 为JS输入值添加模块命名空间
在模块服务器中获取命名空间前缀,传递给JavaScript,确保输入值能被模块正确接收。
2. 优化时区获取触发时机
使用session$onFlushed在应用初始化时仅执行一次时区获取,避免重复执行。
3. 实现UTC时间到本地时区的转换
利用lubridate包将UTC时间转换为客户端本地时区的时间,用于图表展示。
修复后的完整代码
library(shiny) library(shinyjs) library(plotly) library(dplyr) library(lubridate) # JavaScript code(新增命名空间参数) jsCode <- ' shinyjs.getClientTimezone = function(namespace) { const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; // 使用命名空间设置输入值 Shiny.setInputValue(namespace + "client_timezone", timezone); console.log("Client Timezone", timezone); }' # Module UI mod_trend_chart_ui <- function(id) { ns <- NS(id) tagList( shinyjs::useShinyjs(), shinyjs::extendShinyjs(text = jsCode, functions = "getClientTimezone"), plotlyOutput(ns("trend_chart")) ) } # Module Server mod_trend_chart_server <- function(id, raw_data, processed_data) { moduleServer(id, function(input, output, session) { ns <- session$ns # 初始化时执行一次时区获取 session$onFlushed(function() { shinyjs::js$getClientTimezone(ns("")) # 传递命名空间前缀 }, once = TRUE) client_timezone <- reactive({ req(input$client_timezone) print(paste("Received client timezone:", input$client_timezone)) input$client_timezone }) # 转换时间为客户端时区 converted_data <- reactive({ req(client_timezone()) tz <- client_timezone() raw <- raw_data() %>% mutate(local_timestamp = with_tz(utc_timestamp, tzone = tz)) processed <- processed_data() %>% mutate(local_timestamp = with_tz(utc_timestamp, tzone = tz)) list(raw = raw, processed = processed) }) output$trend_chart <- renderPlotly({ req(converted_data()) data <- converted_data() raw <- data$raw processed <- data$processed if (nrow(raw) == 0 && nrow(processed) == 0) { return(plot_ly() %>% layout(title = "No data available.")) } else { p <- plot_ly() %>% add_trace( data = raw, x = ~local_timestamp, y = ~value, name = "原始数据", type = "scatter", mode = "lines", line = list(color = "#f69a8e", shape = "hv"), hovertemplate = "时间:%{x}<br>数值:%{y:.2f}" ) %>% add_trace( data = processed, x = ~local_timestamp, y = ~value, name = "处理后数据", type = "scatter", mode = "lines", line = list(color = "#6bbcf3", shape = "hv"), hovertemplate = "时间:%{x}<br>数值:%{y:.2f}" ) %>% layout(xaxis = list(title = "时间"), yaxis = list(title = "数值")) return(p) } }) }) } # Sample data raw_data_sample <- data.frame( utc_timestamp = as.POSIXct(c("2023-06-01 10:00:00", "2023-06-01 11:00:00", "2023-06-01 12:00:00"), tz = "UTC"), value = c(10, 20, 30) ) processed_data_sample <- data.frame( utc_timestamp = as.POSIXct(c("2023-06-01 10:00:00", "2023-06-01 11:00:00", "2023-06-01 12:00:00"), tz = "UTC"), value = c(15, 25, 35) ) # Main UI ui <- fluidPage( mod_trend_chart_ui("trendChart") ) # Main Server server <- function(input, output, session) { raw_data <- reactiveVal(raw_data_sample) processed_data <- reactiveVal(processed_data_sample) mod_trend_chart_server("trendChart", raw_data, processed_data) } # Run the app shinyApp(ui, server)
关键修复点说明
- 命名空间处理:通过向JS函数传递模块命名空间前缀,确保
client_timezone输入值能被模块正确捕获。 - 初始化执行:使用
session$onFlushed(once = TRUE)保证时区获取仅在应用初始化时执行一次,避免重复调用。 - 时区转换:新增
converted_datareactive,将UTC时间转换为客户端本地时区时间,满足原始需求并正确渲染图表。
内容的提问来源于stack exchange,提问作者Golem
相关产品推荐
相关产品推荐

