我在尝试用模块把应用分成多个页面,但现在无法再从子模块访问输入了

编程语言 2026-07-12

为了重现我当前应用中的错误,我做了一个简单的玩具示例来解释我想解决的问题。

我想要将我的 app1.R 里的页面变成模块(以便更好地组织,正如 https://mastering-shiny.org/scaling-packaging.html 中所建议的那样)。

app1.R

library(shiny)

ui <- navbarPage(
  "My Fancy App",
  tabPanel("page1",
           fluidPage(
             mainPanel(
               titlePanel("Page-1"),
               dataUI("data1"),
               verbatimTextOutput("choice1")
               )
             )
           ),
  tabPanel("page2",
           fluidPage(
             mainPanel(
               titlePanel("Page-2"),
               dataUI("data2"),
               verbatimTextOutput("choice2")
             )
           )
  )
)

server <- function(input, output, session) {
  data1 <- dataServer("data1")
  output$choice1 <- renderText({paste0("Your choice is: ", data1())})
  data2 <- dataServer("data2")
  output$choice2 <- renderText({paste0("Your choice is: ", data2())})
}

shinyApp(ui, server)

R\data.R

dataUI <- function(id) {
  selectInput(NS(id, "dataset"), "Pick a letter", choices = LETTERS)
}

dataServer <- function(id) {
  moduleServer(id, function(input, output, session) {
    out <- reactive(input$dataset)
  })
}

我的尝试是创建两个模块(page1.Rpage2.R),并在我新的 app2.R 中使用它们,但我现在看不到页面底部的文本输出。在调试时我发现 input$dataset 不再像分割前那样被读取。现在,data.R 已经成了新 page*.R 模块的某种“子模块”。

现在我怎么访问它的值?由于我的实际应用相当复杂,我想知道是否有一个巧妙的方法来实现这一点(例如通过将父会话的输入等传递给子模块,或类似的方式)。

app2.R

library(shiny)

ui <- navbarPage(
  "My Fancy App",
  tabPanel("page1", page1UI("tab1")),
  tabPanel("page2", page2UI("tab2")),
)

server <- function(input, output, session) {
  page1Server("tab1")
  page2Server("tab2")
}

shinyApp(ui, server)

R\page1.R

page1UI <- function(id) {
  fluidPage(
    mainPanel(
      titlePanel("Page-1"),
      dataUI("data1"),
      verbatimTextOutput("choice1")
    )
  )
}

page1Server <- function(id) {
  moduleServer(id, function(input, output, session) {
    data <- dataServer("data1")
    output$choice1 <- renderText({paste0("Your choice is: ", data())})
  })
}

R\page2.R

page2UI <- function(id) {
  fluidPage(
    mainPanel(
      titlePanel("Page-2"),
      dataUI("data2"),
      textOutput("choice2")
    )
  )
}

page2Server <- function(id) {
  moduleServer(id, function(input, output, session) {
    data <- dataServer("data2")
    output$choice2 <- renderText({paste0("Your choice is: ", data())})
  })
}

解决方案

我想我找到了答案:我需要在模块中的输入前加上命名空间,例如 dataUI(NS(id,"data1"))

下面是更正后的代码:

R\data.R

dataUI <- function(id) {
  selectInput(NS(id,"dataset"), "Pick a letter", choices = LETTERS)
}

dataServer <- function(id) {
  moduleServer(id, function(input, output, session) {
    return(reactive(input$dataset))
  })
}

R\page1.R

page1UI <- function(id) {
  fluidPage(
    mainPanel(
      titlePanel("Page-1"),
      dataUI(NS(id,"data1")),
      verbatimTextOutput(NS(id,"choice1"))
    )
  )
}

page1Server <- function(id) {
  moduleServer(id, function(input, output, session) {
    data <- dataServer("data1")
    output$choice1 <- renderText({paste0("Your choice is: ", data())})
  })
}

R\page2.R

page2UI <- function(id) {
  fluidPage(
    mainPanel(
      titlePanel("Page-2"),
      dataUI(NS(id,"data2")),
      textOutput(NS(id,"choice2"))
    )
  )
}

page2Server <- function(id) {
  moduleServer(id, function(input, output, session) {
    data <- dataServer("data2")
    output$choice2 <- renderText({paste0("Your choice is: ", data())})
  })
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章