Search code examples
rshinyr-markdownreactiveflexdashboard

Use of conditionalPanel with flexdashboard and runtime shiny to conditionally switch output type based upon column presence


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.


Solution

  • 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