将 UI 插入 R shiny moduleServer
Inserting UI into R shiny moduleServer
我正在尝试允许用户从另一个模块 (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
只有return一个对象,如果你想return多个组件,使用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的官方文档,他们告诉你你需要在模块UI上添加ns(id)
而你不需要为模块服务器添加,但他们没有告诉你为什么。
当您调用 moduleServer(id, function(input, output, session) {...})
时,此处的会话与顶级会话不同。如果您在此处检查会话,它是一个 session_proxy
class 对象,不再是 ShinySession
class 对象。这就是闪亮内部如何知道 moduleServer
下的表达式在模块内部的方式。在模块作用域下调用input
、output
、renderXX
、updateXX
方法时,会先查询session
对象,如果是session_proxy
,先调用session$ns
附加命名空间。这就是为什么你不需要在服务器上添加 ns()
的原因,shiny 为你做了。
好了,我们回到嵌套模块问题。让我们通过这个简单的例子看看 UI 和服务器上的命名空间是什么。它所做的只是在 UI 和服务器上打印出命名空间。
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)
结果UI
服务器
[1] "level1-"
[1] "level1-level2-"
看出区别了吗?我没有为二级服务器调用 ns
,只调用 serverMod2("level2")
,但命名空间已自动附加给我。但是,shiny 不知道将其应用于 UI,所以我必须使用 uiMod2(ns("level2"))
.
shiny之所以不知道,全在于session
。创建模块时需要传入function(input, output, session)
,而创建嵌套模块时,二级模块的这个session
不是ShinySession
,而是一个session_proxy
, 因此将自动附加父命名空间。
UI 情况不同。通常当你像uiMod2 <- function(id) ...
一样创建模块UI时,你没有传递会话对象,所以它不知道要继承什么命名空间。
我正在尝试允许用户从另一个模块 (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
只有return一个对象,如果你想return多个组件,使用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的官方文档,他们告诉你你需要在模块UI上添加ns(id)
而你不需要为模块服务器添加,但他们没有告诉你为什么。
当您调用 moduleServer(id, function(input, output, session) {...})
时,此处的会话与顶级会话不同。如果您在此处检查会话,它是一个 session_proxy
class 对象,不再是 ShinySession
class 对象。这就是闪亮内部如何知道 moduleServer
下的表达式在模块内部的方式。在模块作用域下调用input
、output
、renderXX
、updateXX
方法时,会先查询session
对象,如果是session_proxy
,先调用session$ns
附加命名空间。这就是为什么你不需要在服务器上添加 ns()
的原因,shiny 为你做了。
好了,我们回到嵌套模块问题。让我们通过这个简单的例子看看 UI 和服务器上的命名空间是什么。它所做的只是在 UI 和服务器上打印出命名空间。
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)
结果UI
服务器
[1] "level1-"
[1] "level1-level2-"
看出区别了吗?我没有为二级服务器调用 ns
,只调用 serverMod2("level2")
,但命名空间已自动附加给我。但是,shiny 不知道将其应用于 UI,所以我必须使用 uiMod2(ns("level2"))
.
shiny之所以不知道,全在于session
。创建模块时需要传入function(input, output, session)
,而创建嵌套模块时,二级模块的这个session
不是ShinySession
,而是一个session_proxy
, 因此将自动附加父命名空间。
UI 情况不同。通常当你像uiMod2 <- function(id) ...
一样创建模块UI时,你没有传递会话对象,所以它不知道要继承什么命名空间。