我试图允许用户从另一个模块(ui_module1)添加在server_module2中看到的连续UI输出。因此,当他们点击按钮时,他们将看到 3 个 UI 对象:textOutput、sliderInput、textInput。我下面的代码在输出 UI 对象时停止。如果我不将它包装在第一个模块中,它就可以正常工作。
谢谢。
library(shiny)
library(shinydashboard)
library(shinydashboardPlus)
library(shinyWidgets)
library(dplyr)
ui_module2 = function(id) {
ns = NS(id)
uiOutput(ns("finalOut"))
}
server_module2 = function(id) {
moduleServer(id,
function(input, output, session) {
output$finalOut = renderUI({
ns = session$ns
textOutput(p(style = "color: red", paste0("Package Num:", id, sep = "-")))
sliderInput("n", "N", 1, 1000, 500)
textInput("label", "Label")
})
})
}
ui_module1 = function(id) {
ns = NS(id)
actionBttn(ns("actionbutton1"), "Press Button For New UI")
}
server_module1 = function(id) {
moduleServer(id,
function(input, output, session) {
observeEvent(input$actionbutton1, {
i = sprintf('%04d', input$actionbutton1)
id = sprintf('static%s', i)
print(id)
insertUI(selector = "#actionbutton1",
where = "afterEnd",
ui = ui_module2(id))
server_module2(id)
})
})
}
ui = fluidPage(ui_module1("opt)"))
server = function(input, output, session) {
server_module1("opt)")
}
shinyApp(ui, server)
最佳答案
这是工作代码。很多事情你需要注意。以下是一些要点。
- 记得添加
ns
适用于所有模块。 -
renderUI
只返回一个对象,如果要返回多个组件,请使用tagList
或div
. -
insertUI
模块内的选择器还需要添加ns
.
自己检查代码是否有小错误。
library(shiny)
library(shinydashboard)
library(shinydashboardPlus)
library(shinyWidgets)
library(dplyr)
ui_module2 = function(id) {
ns = NS(id)
uiOutput(ns("finalOut"))
}
server_module2 = function(id) {
moduleServer(id,
function(input, output, session) {
output$finalOut = renderUI({
ns = session$ns
div(
# not sure what you want with next line, commented, fix your own
# textOutput(p(style = "color: red", paste0("Package Num:", id, sep = "-"))),
sliderInput(ns("n"), "N", 1, 1000, 500),
textInput(ns("label"), "Label")
)
})
})
}
ui_module1 = function(id) {
ns = NS(id)
actionBttn(ns("actionbutton1"), "Press Button For New UI")
}
server_module1 = function(id) {
moduleServer(id,
function(input, output, session) {
ns <- session$ns
observeEvent(input$actionbutton1, {
i = sprintf('%04d', input$actionbutton1)
id = sprintf('static%s', i)
print(id)
insertUI(selector = paste0("#", ns("actionbutton1")),
where = "afterEnd",
immediate = TRUE,
ui = ui_module2(ns(id)))
server_module2(id)
})
})
}
ui = fluidPage(ui_module1("opt"))
server = function(input, output, session) {
server_module1("opt")
}
shinyApp(ui, server)
了解它是如何工作的
本例是嵌套模块的示例。要理解这一点,您需要知道如何ns
命名空间在 Shiny 中工作。
作为Shiny的官方文档,他们告诉你需要添加ns(id)
在模块 UI 上,您不需要为模块服务器执行此操作,但他们没有告诉您原因。
当您调用moduleServer(id, function(input, output, session) {...})
时,这里的 session 与您的顶级 session 不同。如果你检查这里的 session ,它是 session_proxy
类对象,不再是ShinySession
类对象。这就是shiny内部如何知道moduleServer
下的表达式的方式。位于模块内部。当您调用input
时, output
, renderXX
, updateXX
模块范围下的方法,它会首先查询session
对象,如果它是 session_proxy
,调用session$ns
首先附加命名空间。这就是为什么您不需要添加 ns()
在服务器上,shiny 已经为你做到了。
好吧,让我们回到嵌套模块问题。让我们通过这个简单的例子来看看 UI 和服务器上的命名空间是什么。它所做的只是打印出 UI 和服务器上的 namespace 。
library(shiny)
uiMod2 <- function(id) {
ns <- NS(id)
div(id = ns(""))
}
serverMod2 <- function(id) {
moduleServer(
id,
function(input, output, session) {
ns <- session$ns
print(ns(""))
})
}
uiMod1 <- function(id) {
ns <- NS(id)
tagList(
div(id = ns("")),
uiMod2(ns("level2"))
)
}
serverMod1 <- function(id) {
moduleServer(
id,
function(input, output, session) {
ns <- session$ns
print(ns(""))
serverMod2("level2")
})
}
ui <- fluidPage(uiMod1("level1"))
server <- function(input, output, session) {serverMod1("level1")}
shinyApp(ui, server)
结果用户界面
服务器
[1] "level1-"
[1] "level1-level2-"
看出区别了吗?我没有打电话ns
对于二级服务器,仅限 serverMod2("level2")
,但 namespace 会自动附加给我。然而,shiny 不知道将其应用到 UI,所以我必须使用 uiMod2(ns("level2"))
.
shiny不知道的原因全是session
。创建模块时需要传递function(input, output, session)
,当您创建嵌套模块时,此 session
对于第二级模块不是 ShinySession
,但是 session_proxy
,因此父命名空间将自动添加。
UI 的情况有所不同。通常当您创建类似 uiMod2 <- function(id) ...
的模块 UI 时,你没有传递session对象,所以它不知道要继承什么命名空间。
关于r - 将 UI 插入 R Shiny 模块服务器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70760041/