5

我正在尝试第一次Shiny使用创建应用程序。使用模块构建应用程序,以帮助保持大型应用程序模块化。但是,默认情况下,模块不会相互通信。我想跨模块共享数据。根据文档,对象是跨模块共享数据的有用方式golemgolemShinyShinygolemR6

但是,在文档中提供的示例中golem,不清楚将R6生成器放在哪里。根据Appsilon的说法,R6生成器进入一个单独的.R文件(例如logger_manager.R),然后调用global.R以从类中构造一个新对象:logger_manager = LoggerManager$new(). 但是,基于- 的应用程序中没有global.R文件。golemShiny

下面是golem基于我的Shiny应用程序的最小示例。我尝试遵循文档中提供的示例中golem的结构,但它似乎没有跨模块共享数据:

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
  }

但是,不能跨模块共享数据,因为当我尝试resp_id在模块 B(在模块 A 中生成)中打印时,我收到以下错误:

An error has occurred!
object 'questionnaire_responses' not found
4

2 回答 2

3

所以这里的问题是,当您的 R6 对象传递给服务器函数时,您正试图从 UI 函数打印对象。

换句话说,mod_b_ui当 R6 对象被传递给mod_b_server函数时,您正试图在函数中进行打印。

对于您的原始问题,您可以在标准文件中创建 R6 类,如https://github.com/ColinFay/golemexamples/blob/master/golemR6/R/R6.R

请注意,尽管您的数据对象不是响应式的,并且在您更改模块 A 时不会重新打印 - 这是您可以使用{gargoyle}的地方,例如:

有关完整的工作示例,请参阅https://github.com/ColinFay/golemexamples/tree/master/golemR6

PS:您的模块中缺少 ns() :)

科林

于 2021-04-13T19:30:26.813 回答
2

我对你的代码做了一些更正(一些错字)

tl;dr:您无法在 UI 中访问问卷调查。

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
}
于 2021-04-13T19:22:47.677 回答