2016-08-23 64 views
5

我想模块化一个复杂的Shiny应用程序,我有一个conditionalPanel应该只显示给定的输入状态。R带条件面板和反应的闪亮模块

之前我做了一切模块化,输入和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) 

我认为这应该工作,但它没有 - 如果我再拍参考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引用本身没有。

任何想法我应该如何让这个条件面板正常工作?

谢谢。

编辑:原来并不是一个真正的错误:https://github.com/rstudio/shiny/issues/1318。在下面看到我自己的答案。

但也请注意,我实际上喜欢接受答案中给出的renderUI解决方案比我的原始conditionalPanel方法更好。

回答

4

调用模块后,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) 

编辑:

这是行得通的,但是这不违反模块化的概念吗?您必须知道模块的代码在内部调用输入'select'才能构建'id1-select'。

是的,你说得对。

根据这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) 
+0

这确实有效,但是这不违反模块化的概念吗?您必须知道模块的代码在内部调用输入'select'才能构建'id1-select'。 –

+0

是的,你是对的 - 我更新了我的文章。 –

+1

我认为这确实是一个错误。我通过https://github.com/rstudio/shiny/issues/1318提供了一个更简单的例子。我更喜欢'renderUI'解决方案,因为'conditionalPanel'依赖于'display:none'风格来隐藏内容,而'renderUI'方法只是给出一个空的div。谢谢你写了。 –

2

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

右 - 如果默认情况下,我们不计算/呈现输出值,如果它们不会被看到的话。如果我们不计算它们,你不能在条件下使用它们。

可以通过设置输出来计算每一次,你可以在你server.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 
})