带有conditionalPanel和被动反应的R Shiny模块

时间:2016-08-23 19:56:18

标签: r module shiny

我正在尝试模块化一个复杂的Shiny应用程序,我的conditionalPanel应该只出现给定的输入状态。

在我将所有内容模块化之前,输入和conditionalPanel都在ui.R中,我可以使用以下内容引用输入:

conditionalPanel("input.select == 'Option one'", p('Option one is selected'))

现在我已经模块化了,访问输入更加复杂。我认为以下是这样做的方法,但它并不是很有效。 (这里我把东西组合成一个独立的脚本):

library(shiny)

## Module code for 'selectorUI' and 'selector'
selectorUI <- function(id) {
  ns <- NS(id)
  selectizeInput(inputId = ns('select'),
                 label = 'Make a choice:',
                 choices = c('Option one', 'Option two'))
}

selector <- function(input, output, session) {
  reactive(input$select)
}

## Main app
ui <- shinyUI(fluidPage(
  selectorUI('id1'),
  conditionalPanel(condition = "output.selected == 'Option one'", p('Option one is selected.'))
))

server <- shinyServer(function(input, output, session) {
  output$selected <- callModule(selector, 'id1')
})

shinyApp(ui = ui, server = server)

我认为这应该有效,但事实并非如此 - 只有在主ui部分中对output$selected进行另一次引用时才有效:

ui <- shinyUI(fluidPage(
  selectorUI('id1'),
  textOutput('selected'),   ## Adding just this one line makes the next line work
  conditionalPanel(condition = "output.selected == 'Option one'", p('Option one is selected.'))
))

不幸的是,当然这会产生渲染textOutput('selected')结果的不良影响。我只能猜测它起作用的原因是因为它以某种方式触发反应,而JavaScript引用本身并没有。

知道如何让这个conditionalPanel正常工作吗?

谢谢..

编辑:原来并不是一个错误:https://github.com/rstudio/shiny/issues/1318。请参阅下面的答案。

但请注意,我实际上更喜欢接受答案中提供的renderUI解决方案,而不是原来的conditionalPanel方法。

2 个答案:

答案 0 :(得分:6)

调用模块后,selectizeInput的ID为id1-select。在javaScript中,有两种访问对象属性的方法:

objectName.propertyobjectName['property']

由于-ID我们必须通过字符串引用它,所以第二种方法是可行的。

conditionalPanel中的条件变为:

input['id1-select'] == 'Option one'

完整示例:

library(shiny)

## Module code for 'selectorUI' and 'selector'
selectorUI <- function(id) {
  ns <- NS(id)
  selectizeInput(inputId = ns('select'),
                 label = 'Make a choice:',
                 choices = c('Option one', 'Option two'))
}

## Main app
ui <- shinyUI(fluidPage(
  selectorUI('id1'),
  conditionalPanel(condition = "input['id1-select'] == 'Option one'",
                   p('Option one is selected.'))
))

server <- shinyServer(function(input, output, session) {

})

shinyApp(ui = ui, server = server)

修改

  

这确实有效,但是它是否违反了模块化的概念?您必须知道模块的内部调用该输入的代码&#39;选择&#39;为了构建&#39; id1-select&#39;。

是的,你是对的。

根据这个article,您使用的技巧,即将模块调用分配给所选的输出$,然后通过output.selected在客户端访问其值应该可以,但它不会。我不知道为什么......这可能是一个错误。 (我有来自github的最新闪亮版本)

我唯一能想到的是使用renderUI代替conditionalPanel,如下例所示:

library(shiny)
## Module code for 'selectorUI' and 'selector'
selectorUI <- function(id) {
  ns <- NS(id)
  selectizeInput(inputId = ns('select'),
                 label = 'Make a choice:',
                 choices = c('Option one', 'Option two'))
}

selector <- function(input, output, session) {
  reactive(input$select)
}

## Main app
ui <- shinyUI(fluidPage(
  selectorUI('id1'),
  uiOutput("dynamic1")
))

server <- shinyServer(function(input, output, session) {


  output$dynamic1 <- renderUI({
    condition1 <- callModule(selector, 'id1') # or just callModule(selector, 'id1')()
    if (condition1() == 'Option one') return(p('Option one is selected.'))
  })

})

shinyApp(ui = ui, server = server)

答案 1 :(得分:3)

原来它实际上不是一个bug,只是有点棘手。 According to Joe Cheng

  

正确 - 默认情况下,如果它们不可见,我们不会计算/渲染输出值。如果我们不计算它们,你就不能在条件下使用它们。

     

您可以通过设置每次计算输出来更改此行为,您可以在服务器中使用它.R(将outputId替换为相应的值):

     

outputOptions(output, "outputId", suspendWhenHidden = FALSE)

因此,为了解决原始示例的问题,我们只需要将一行添加到服务器函数中:

server <- shinyServer(function(input, output, session) {
  output$selected <- callModule(selector, 'id1')
  outputOptions(output, 'selected', suspendWhenHidden = FALSE) # Adding this line
})