r - 将 UI 插入 R Shiny 模块服务器

标签 r user-interface shiny module shiny-reactivity

我试图允许用户从另一个模块(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)

最佳答案

这是工作代码。很多事情你需要注意。以下是一些要点。

  1. 记得添加ns适用于所有模块。
  2. renderUI只返回一个对象,如果要返回多个组件,请使用tagListdiv .
  3. 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)

结果用户界面

enter image description here

服务器

[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/

相关文章:

ios - UIImageView 与 DownArrow.png 具有奇怪的行为

r - 为什么更新数据表后数据表中的 R/Shiny 输入无法正常工作?

R:如何获取每个日期和小时的频率计数

r - 在 R 中合并每月日期

java - 如何创建一个java程序让用户使用密码登录并存储数据

c - 从 QT 运行 C 程序

r - conditionalPanel Shiny 隐藏复选框选项

r - 有没有办法在 Shiny 的情况下调用关闭服务器进程的函数?

r - 使用 .id 使用 purrr::map_df 制作输入项列,无需复制命名向量的输入

r - 基于整数按因子排序 data.frame