如何在 golem Shiny 应用程序中包含 R6 对象以跨模块共享数据
How to include R6 objects to share data across modules in golem Shiny app
我第一次尝试使用 golem
创建一个 Shiny
应用程序。 golem
使用模块构建 Shiny
应用程序以帮助保持大型 Shiny
应用程序模块化。但是,默认情况下,模块之间不会相互通信。我想跨模块共享数据。根据 golem
文档,R6
objects are a useful way to share data across modules.
但是,在 example provided in the golem
documentation, it is unclear where to put the R6
generator. According to Appsilon 中,R6
生成器进入一个单独的 .R
文件(例如,logger_manager.R
),并在 [=27] 中调用=] 从 class 构造一个新对象:logger_manager = LoggerManager$new()
。但是,基于 golem
的 Shiny
应用程序中没有 global.R
文件。
下面是我的基于 golem
的 Shiny
应用程序的最小示例。我试图遵循 example provided in the golem
documentation 中的结构,但它似乎并没有跨模块共享数据:
app_ui.R
:
app_ui <- function(request) {
tagList(
# Leave this function for adding external resources
golem_add_external_resources(),
# List the first level UI elements here
fluidPage(
mod_a_ui("a_ui_1"),
mod_b_ui("b_ui_1")
)
)
}
golem_add_external_resources <- function(){
add_resource_path(
'www', app_sys('app/www')
)
tags$head(
favicon(),
bundle_resources(
path = app_sys('app/www'),
app_title = 'Test'
),
# Add here other external resources
# for example, you can add shinyalert::useShinyalert()
shinyjs::useShinyjs()
)
}
app_server.R
:
app_server <- function( input, output, session ) {
# Generate R6 Class
QuestionnaireResponses <- R6Class(
classname = "QuestionnaireResponses",
public = list(
resp_id = NULL,
timezone = NULL,
timestamp = NULL,
gender = NULL,
)
)
# Create new object to share data across modules using the R6 Class
questionnaire_responses <- QuestionnaireResponses$new()
# List the first level callModules here
callModule(mod_a_server, "a_ui_1", questionnaire_responses)
callModule(mod_b_server, "b_ui_1", questionnaire_responses)
}
mod_a.R
:
mod_a_ui <- function(id){
ns <- NS(id)
tagList(
radioButtons(inputId = "gender",
label = "What is your sex?",
choices = c("Male" = 1,
"Female" = 2),
selected = character(0))
)
}
mod_a_server <- function(input, output, session, questionnaire_responses){
ns <- session$ns
# Add time start to the output vector
timestamp <- format(Sys.time(), "%Y-%m-%d %H:%M:%OS6")
timezone <- Sys.timezone()
# Generate a survey-specific ID number
resp_id <- paste0(sample(c(letters, LETTERS, 0:9), 10), collapse = "")
# Assign values to R6 object
questionnaire_responses$resp_id <- resp_id
questionnaire_responses$timezone <- timezone
questionnaire_responses$timestamp <- timestamp
questionnaire_responses$gender <- input.gender
}
mod_b.R
:
mod_b_ui <- function(id){
ns <- NS(id)
tagList(
print("questionnaire_responses$resp_id")
)
}
mod_b_server <- function(input, output, session, questionnaire_responses){
ns <- session$ns
}
但是,数据不能跨模块共享,因为当我尝试在模块 B(在模块 A 中生成)中打印 resp_id
时,我收到以下错误:
An error has occurred!
object 'questionnaire_responses' not found
我对你的代码做了一些更正(一些错字)
tl;dr:您无法在 UI.
中访问 questionnaire_responses
app_ui.R
:
same as you
app_server.R
:
#' @import shiny
#' @import R6
#' @noRd
app_server <- function( input, output, session ) {
# Generate R6 Class
QuestionnaireResponses <- R6Class(
classname = "QuestionnaireResponses",
public = list(
resp_id = NULL,
timezone = NULL,
timestamp = NULL,
gender = NULL
)
)
# Create new object to share data across modules using the R6 Class
questionnaire_responses <- QuestionnaireResponses$new()
# List the first level callModules here
callModule(mod_a_server, "a_ui_1", questionnaire_responses)
callModule(mod_b_server, "b_ui_1", questionnaire_responses)
}
mod.R
:
mod_a_ui <- function(id){
ns <- NS(id)
tagList(
radioButtons(inputId = ns("gender"),
label = "What is your sex?",
choices = c("Male" = 1,
"Female" = 2),
selected = character(0))
)
}
mod_a_server <- function(input, output, session, questionnaire_responses){
ns <- session$ns
# Add time start to the output vector
timestamp <- format(Sys.time(), "%Y-%m-%d %H:%M:%OS6")
timezone <- Sys.timezone()
# Generate a survey-specific ID number
resp_id <- paste0(sample(c(letters, LETTERS, 0:9), 10), collapse = "")
# Assign values to R6 object
questionnaire_responses$resp_id <- resp_id
questionnaire_responses$timezone <- timezone
questionnaire_responses$timestamp <- timestamp
questionnaire_responses$gender <- "input$gender"
}
mod_b_ui <- function(id){
ns <- NS(id)
tagList(
print("questionnaire_responses$resp_id") # you can't access questionnaire_responses in the UI
)
}
mod_b_server <- function(input, output, session, questionnaire_responses){
ns <- session$ns
print(questionnaire_responses$resp_id) # but here you can
}
所以这里的问题是,当您的 R6 对象被传递 到服务器时,您试图从 UI 函数 打印对象 函数.
换句话说,当 R6 对象被传递给 mod_b_server
函数时,您正试图在 mod_b_ui
函数中进行打印。
对于您的原始问题,您可以在标准文件中创建 R6 class,如 https://github.com/ColinFay/golemexamples/blob/master/golemR6/R/R6.R
请注意,尽管您的数据对象 不是反应性的 并且在您更改模块 A 时不会重新打印 — 例如,您可以在此处使用 {gargoyle}
:
- 初始化https://github.com/ColinFay/golemexamples/blob/master/golemR6/R/app_server.R#L12
- 触发器https://github.com/ColinFay/golemexamples/blob/master/golemR6/R/mod_a.R#L43
- 观看https://github.com/ColinFay/golemexamples/blob/master/golemR6/R/mod_b.R#L22
请参阅 https://github.com/ColinFay/golemexamples/tree/master/golemR6 以获取完整的工作示例。
PS:您的模块中缺少 ns():)
科林
我第一次尝试使用 golem
创建一个 Shiny
应用程序。 golem
使用模块构建 Shiny
应用程序以帮助保持大型 Shiny
应用程序模块化。但是,默认情况下,模块之间不会相互通信。我想跨模块共享数据。根据 golem
文档,R6
objects are a useful way to share data across modules.
但是,在 example provided in the golem
documentation, it is unclear where to put the R6
generator. According to Appsilon 中,R6
生成器进入一个单独的 .R
文件(例如,logger_manager.R
),并在 [=27] 中调用=] 从 class 构造一个新对象:logger_manager = LoggerManager$new()
。但是,基于 golem
的 Shiny
应用程序中没有 global.R
文件。
下面是我的基于 golem
的 Shiny
应用程序的最小示例。我试图遵循 example provided in the golem
documentation 中的结构,但它似乎并没有跨模块共享数据:
app_ui.R
:
app_ui <- function(request) {
tagList(
# Leave this function for adding external resources
golem_add_external_resources(),
# List the first level UI elements here
fluidPage(
mod_a_ui("a_ui_1"),
mod_b_ui("b_ui_1")
)
)
}
golem_add_external_resources <- function(){
add_resource_path(
'www', app_sys('app/www')
)
tags$head(
favicon(),
bundle_resources(
path = app_sys('app/www'),
app_title = 'Test'
),
# Add here other external resources
# for example, you can add shinyalert::useShinyalert()
shinyjs::useShinyjs()
)
}
app_server.R
:
app_server <- function( input, output, session ) {
# Generate R6 Class
QuestionnaireResponses <- R6Class(
classname = "QuestionnaireResponses",
public = list(
resp_id = NULL,
timezone = NULL,
timestamp = NULL,
gender = NULL,
)
)
# Create new object to share data across modules using the R6 Class
questionnaire_responses <- QuestionnaireResponses$new()
# List the first level callModules here
callModule(mod_a_server, "a_ui_1", questionnaire_responses)
callModule(mod_b_server, "b_ui_1", questionnaire_responses)
}
mod_a.R
:
mod_a_ui <- function(id){
ns <- NS(id)
tagList(
radioButtons(inputId = "gender",
label = "What is your sex?",
choices = c("Male" = 1,
"Female" = 2),
selected = character(0))
)
}
mod_a_server <- function(input, output, session, questionnaire_responses){
ns <- session$ns
# Add time start to the output vector
timestamp <- format(Sys.time(), "%Y-%m-%d %H:%M:%OS6")
timezone <- Sys.timezone()
# Generate a survey-specific ID number
resp_id <- paste0(sample(c(letters, LETTERS, 0:9), 10), collapse = "")
# Assign values to R6 object
questionnaire_responses$resp_id <- resp_id
questionnaire_responses$timezone <- timezone
questionnaire_responses$timestamp <- timestamp
questionnaire_responses$gender <- input.gender
}
mod_b.R
:
mod_b_ui <- function(id){
ns <- NS(id)
tagList(
print("questionnaire_responses$resp_id")
)
}
mod_b_server <- function(input, output, session, questionnaire_responses){
ns <- session$ns
}
但是,数据不能跨模块共享,因为当我尝试在模块 B(在模块 A 中生成)中打印 resp_id
时,我收到以下错误:
An error has occurred!
object 'questionnaire_responses' not found
我对你的代码做了一些更正(一些错字)
tl;dr:您无法在 UI.
中访问 questionnaire_responsesapp_ui.R
:
same as you
app_server.R
:
#' @import shiny
#' @import R6
#' @noRd
app_server <- function( input, output, session ) {
# Generate R6 Class
QuestionnaireResponses <- R6Class(
classname = "QuestionnaireResponses",
public = list(
resp_id = NULL,
timezone = NULL,
timestamp = NULL,
gender = NULL
)
)
# Create new object to share data across modules using the R6 Class
questionnaire_responses <- QuestionnaireResponses$new()
# List the first level callModules here
callModule(mod_a_server, "a_ui_1", questionnaire_responses)
callModule(mod_b_server, "b_ui_1", questionnaire_responses)
}
mod.R
:
mod_a_ui <- function(id){
ns <- NS(id)
tagList(
radioButtons(inputId = ns("gender"),
label = "What is your sex?",
choices = c("Male" = 1,
"Female" = 2),
selected = character(0))
)
}
mod_a_server <- function(input, output, session, questionnaire_responses){
ns <- session$ns
# Add time start to the output vector
timestamp <- format(Sys.time(), "%Y-%m-%d %H:%M:%OS6")
timezone <- Sys.timezone()
# Generate a survey-specific ID number
resp_id <- paste0(sample(c(letters, LETTERS, 0:9), 10), collapse = "")
# Assign values to R6 object
questionnaire_responses$resp_id <- resp_id
questionnaire_responses$timezone <- timezone
questionnaire_responses$timestamp <- timestamp
questionnaire_responses$gender <- "input$gender"
}
mod_b_ui <- function(id){
ns <- NS(id)
tagList(
print("questionnaire_responses$resp_id") # you can't access questionnaire_responses in the UI
)
}
mod_b_server <- function(input, output, session, questionnaire_responses){
ns <- session$ns
print(questionnaire_responses$resp_id) # but here you can
}
所以这里的问题是,当您的 R6 对象被传递 到服务器时,您试图从 UI 函数 打印对象 函数.
换句话说,当 R6 对象被传递给 mod_b_server
函数时,您正试图在 mod_b_ui
函数中进行打印。
对于您的原始问题,您可以在标准文件中创建 R6 class,如 https://github.com/ColinFay/golemexamples/blob/master/golemR6/R/R6.R
请注意,尽管您的数据对象 不是反应性的 并且在您更改模块 A 时不会重新打印 — 例如,您可以在此处使用 {gargoyle}
:
- 初始化https://github.com/ColinFay/golemexamples/blob/master/golemR6/R/app_server.R#L12
- 触发器https://github.com/ColinFay/golemexamples/blob/master/golemR6/R/mod_a.R#L43
- 观看https://github.com/ColinFay/golemexamples/blob/master/golemR6/R/mod_b.R#L22
请参阅 https://github.com/ColinFay/golemexamples/tree/master/golemR6 以获取完整的工作示例。
PS:您的模块中缺少 ns():)
科林