我正在尝试模块化一个 Shiny 的应用程序。到目前为止,一切进展顺利,但我在设计一个具有两个模块 A 和 B 的系统时遇到了麻烦,其中 A 需要来自 B 的数据,B 需要来自 A 的数据。
首先,请关注 this tutorial ( Shiny 版本 1.5)我让这个非常基本的独立示例可以工作。
library(shiny)
#######################
# FILE MODULE #
# Load and save value #
#######################
fileModuleUI <- function(id) {
ns <- NS(id)
tagList(
fileInput(ns("fileInput"), "Input"),
downloadButton(ns("fileOutput"), "Save problem")
)
}
fileModuleServer <- function(id, textFieldData) {
moduleServer(
id,
function(input, output, session) {
# Write observer
output$fileOutput <- downloadHandler(
filename = function() { "myfile.dcf" },
content = function(file) { dput(textFieldData(), file) }
)
# Read observers
userFile <- reactive({
validate(need(input$fileInput, message = FALSE))
input$fileInput
})
fileContent <- reactive({
dget(userFile()$datapath)
})
return(fileContent)
}
)
}
###############
# MAIN UI #
###############
ui <- fluidPage(
sidebarLayout(
sidebarPanel(fileModuleUI("dataHandler")),
mainPanel(textInput("mainData", label = "Type your data in here"))
)
)
server <- function(input, output, session) {
fileContent <- fileModuleServer("dataHandler", reactive(input$mainData))
observe({
updateTextInput(session, "mainData", value = fileContent())
})
}
shinyApp(ui = ui, server = server)
使用这个漂亮的工具,我可以从文件中的 textInput
加载并保存一行文本。
现在我还想模块化 mainPanel
中的内容。我们将其称为 mainModule
。
虽然构建 mainModuleUI
非常简单,但 mainModuleServer
引入了一些交叉依赖问题:
fileModuleServer
需要知道mainModuleServer
的文本字段,以便将其值保存在文件中mainModuleServer
需要从fileModuleServer
了解文件内容,以便在加载文件时更新其文本输入字段
因此,服务器可能看起来有点像这样:
fileModuleServer <- function(id, textFieldData) { ... }
mainModuleServer <- function(id, fileContent) { ... }
server <- function(input, output, session) {
# what to pass as second parameter?
fileContent <- fileModuleServer("dataHandler", ???)
# would passing fileContent even work?
mainModuleServer("mainPanel", fileContent)
}
有什么好的方法可以解决这个问题吗?
最佳答案
我通过在我的主应用程序中引入 reactiveValue
来让它工作。然后我将该值传递给我的服务器,并通过写入 value('this is some new value')
更改其值,或通过调用 value()
读取其值。
这可能看起来像这样:
# Module A
moduleAServer <- function(id, someData) {
moduleServer(
id,
function(input, output, session) {
# when clicking on load-button, just pretend to load some data
observeEvent(input$load, {
someData('This is the new data!')
})
observeEvent(input$save, {
print(paste('Saving the following:', someData()))
})
}
)
}
# Module B
moduleBServer <- function(id, someData) {
moduleServer(
id,
function(input, output, session) {
# observe will be called when Module A changes the data inside someData
observe({
# not sure if I need this req
req(someData)
print(paste('some Data changed to', someData()))
})
}
)
}
mainServer <- function(input, output, session) {
someData <- reactiveVal('oh')
chosenFile <- moduleAServer('filePanel', someData)
inputServer <- moduleBServer('mainPanel', someData)
}
关于r - 使 Shiny 的应用程序模块彼此共享 react 值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65727477/