1

I am trying to conditionally swith shiny output render types in a flexdashboard tab using conditionalPanel as outlined in the Shiny documentation here, by using a reactive output object and making sure it is returned to the browser by using outputOptions(output, [myOutputName], suspendWhenHidden = FALSE). This approach has been suggested in several SO anwsers (e.g. here and here) for full-fledged Shiny apps but I could not find an example for their use in a Shiny document (R markdown). Essentially, I want to test if a selected data set has a certain column (i.e. the column is not null), and make the UI conditionally render the plot based upon the presence of this column. In the toy data below, the list has a known number of items, and it is known which ones have the missing column. When running the document locally, the desired behavior is there (i.e. the set will switch based upon the selection and the conditionalPanel appears to show what I would like it to show), but still the inspector shows the errors that I listed below. Publishing on rstudio connect leads to the interface just not being rendered (because of the same errors, I presume). Are these errors (Error evaluating expression: ouput.evalseplen == true and Uncaught ReferenceError: ouput is not defined) in the inspector a known shiny bug? Or is something deeper going on?

---
title: "fdb_reprex"
author: "FMKerchof"
runtime: shiny
output:
  flexdashboard::flex_dashboard:
    orientation: rows
    inlcudes:
      navbar:
        - { title: "More info", href: "https://github.com/FMKerckhof", align: right }
fontsize: 9pt
editor_options: 
  chunk_output_type: console
---

```{r setup, include=FALSE}
# Set knitr options ----
knitr::opts_chunk$set(echo = TRUE)
# load packages ----
library(shiny)
library(shinydashboard)
library(ggplot2)
library(plotly)
# toy dataset ----
inputlist <- list(fulliris=iris,
                  irisnoseplen=iris[,-1],
                  irisnopetlen=iris[,c(1,2,4,5)])
```

Inputs {.sidebar}
=======================================================================

```{r datasetsel, echo = FALSE}
renderUI(inputPanel(
  selectInput("datasetsel", label = "Choose your dataset:",
              choices = unique(names(inputlist)),
              selected = unique(names(inputlist))[2])
))

selected_data <- reactive({
  inputlist[[input$datasetsel]]
}) |>  bindEvent(input$datasetsel)
```

Sepals {data-icon="fa-leaf"}
===================================== 

Row {.tabset}
-------------------------------------

### Sepal widths

```{r sepwidthplotly, echo=FALSE}
output$p1 <- renderPlotly({
  req(selected_data())
  p1 <- selected_data() |>
    ggplot(aes(y=Sepal.Width,fill=Species,x=Species)) + geom_boxplot() + theme_bw()
  ggplotly(p1)
})

plotlyOutput("p1", width = "auto", height = "auto") 
```

### Sepal lengths

```{r seplenplotly, echo=FALSE}
output$p2 <- renderPlotly({
  if(!is.null(selected_data()$Sepal.Length)){
  p2 <- selected_data() |>
    ggplot(aes(y=Sepal.Length,fill=Species,x=Species)) + geom_boxplot() + theme_bw()
  ggplotly(p2)
  }
})




output$noseplentext <- renderText({
  if(is.null(selected_data()$Sepal.Length)){
    "No Sepal Lengths in the selected dataset"
  }
})

output$evalseplen <- reactive({
  return(is.null(selected_data()$Sepal.Length))
})

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


conditionalPanel(condition = "ouput.evalseplen == true",
                 textOutput("noseplentext"))
conditionalPanel(condition = "ouput.evalseplen == false",
                 plotlyOutput("p2",width="auto",height="auto"))

```

From the inspector it becomes clear that the output is not defined, but I explicitly asked for it to be returned by setting suspendWhenHidden to FALSE inspector-overview showing that output is not found

My session information: R 4.1.2, shiny 1.7.1, flexdashboard 0.5.2, plotly 4.10.0, ggplot2 2.3.3.5

edit Thanks to the answer below, I realize I made a typo in the conditional statement (ouput in lieu of output), which was also very clear from the error messages.

FM Kerckhof
  • 1,270
  • 1
  • 14
  • 31

1 Answers1

2

I think I found a solution, now I do not use the select input to show/hide the conditional panels.

---
title: "fdb_reprex"
author: "FMKerchof"
runtime: shiny
output:
  flexdashboard::flex_dashboard:
    orientation: rows
    inlcudes:
      navbar:
        - { title: "More info", href: "https://github.com/FMKerckhof", align: right }
fontsize: 9pt
editor_options: 
  chunk_output_type: console
---

```{r setup, include=FALSE}
# Set knitr options ----
knitr::opts_chunk$set(echo = TRUE)
# load packages ----
library(shiny)
library(shinydashboard)
library(ggplot2)
library(plotly)
library(shinyjs)

# toy dataset ----
inputlist <- list(
  fulliris=iris,
  irisnoseplen=iris[,-1],
  irisnopetlen=iris[,c(1,2,4,5)]
  )
```

Inputs {.sidebar}
=======================================================================

```{r datasetsel, echo = FALSE}
renderUI(
  inputPanel(
    selectInput(
      "datasetsel", label = "Choose your dataset:",
      choices = unique(names(inputlist)),
      selected = unique(names(inputlist))[2]),
    )
  )

selected_data <- reactive({
  inputlist[[input$datasetsel]]
  }) |>  
  bindEvent(input$datasetsel)
```

Sepals {data-icon="fa-leaf"}
===================================== 

Row {.tabset}
-------------------------------------

### Sepal widths

```{r sepwidthplotly, echo=FALSE}
output$p1 <- renderPlotly({
  req(selected_data())
  
  p1 <- selected_data() |>
    ggplot(aes(y=Sepal.Width,fill=Species,x=Species)) + 
    geom_boxplot() + 
    theme_bw()
  
  ggplotly(p1)
})

plotlyOutput("p1", width = "auto", height = "auto") 
```

### Sepal lengths

```{r seplenplotly, echo=FALSE}
output$p2 <- renderPlotly({
  p2 <- selected_data() |>
      ggplot(aes(y = Sepal.Length, fill = Species, x = Species)) + 
      geom_boxplot() + 
      theme_bw()
    
    ggplotly(p2)

})

output$evalseplen = reactive({
  is.null(selected_data()$Sepal.Length)
})

output$noseplentext <- renderText({
  "No Sepal Lengths in the selected dataset"
})

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


conditionalPanel(
  condition = "output.evalseplen", 
  textOutput("noseplentext")
  )

conditionalPanel(
  condition = "!output.evalseplen",
  plotlyOutput("p2",width="auto",height="auto")
  )

```

When you select irisnopetlen or fulliris enter image description here

When you select irisnoseplen enter image description here

Johan Rosa
  • 2,797
  • 10
  • 18
  • 1
    HI @Johan Rosa, that does indeed do what I wanted - on the toy dataset I provided. However, in reality `inputlist` will not have a previously known number of items (or with known names), as it is requested from a database. Essentially, I'd like *to render the conditional tab only when a column is present in the selected data*. Your insightful answer made me realize that I did not phrase this correctly above, so I updated my question accordingly. – FM Kerckhof Mar 28 '22 at 07:30
  • 1
    I think I fixed the answer, please check. – Johan Rosa Mar 28 '22 at 10:21
  • You did, thank you very much - indeed there was a typo in my example, which strangely enough resulted still in the desired behavior locally but was correctly handled by RSconnect with throwing an error. Is the library(shinyjs) needed here? – FM Kerckhof Mar 28 '22 at 11:35
  • 1
    No, Is not needed – Johan Rosa Mar 28 '22 at 12:48