Insert condition to show daterange in shiny code

Can you help me with the following question: See in ui , that the daterange are only shown when a file is loaded via fileInput - this is working fine in the code. For this, it is necessary to press the Excel button to show the fileInput . However, when I choose the database option, the files are loaded, but the daterange are not displayed. How can I adjust this?

In case you want to test the Excel option, here is the file: ex.xlsx - Google Sheets

library(shiny)
library(dplyr)
library(shinythemes)

ui <- fluidPage(
  
  shiny::navbarPage(theme = shinytheme("flatly"), collapsible = TRUE,
                    br(),
                    tabPanel("PAGE1",
                             sidebarLayout(
                               sidebarPanel(
                                 radioButtons("button", 
                                              label = h3("Data source"),
                                              choices = list("Excel" = "Excel",
                                                             "Database" = "database"), 
                                              selected = "File"),
                                 br(),br(),
                                 uiOutput('fileInput'),
                                 
                                 conditionalPanel(
                                   condition = "output.fileUploaded == true",
                                   uiOutput("daterange")
              
                               )),
                               mainPanel(
                                 dataTableOutput('table')
                               )))))



server <- function(input, output) {

  eitherdata <- reactiveVal(NULL)
  observeEvent(data(),  eitherdata(data()))
  observeEvent(data2(),  eitherdata(data2()))
  
  output$fileInput <- renderUI({
    ib <-  req(input$button) 
    if(ib=="Excel"){
    fileInput("file",h4("Import file"), multiple = T, accept = ".xlsx")
      
  } else {
        NULL
      }
    
  })
  
  observe({
    req(input$button)  
    if (input$button =="database") {

          con <- DBI::dbConnect(odbc::odbc(),
                                Driver   = "[your driver's name]",
                                Server   = "[your server's path]",
                                Database = "[your database's name]",
                                UID      = rstudioapi::askForPassword("Database user"),
                                PWD      = rstudioapi::askForPassword("Database password"),
                                Port     = 1433)
          
          data <-tbl(con, in_schema("dbo", "date1")) %>%
            collect()
          
          data2 <- tbl(con, in_schema("dbo", "date2")) %>% 
            collect()
               
    }
    })
 

  output$fileUploaded <- reactive({
    return(!is.null(data()))
  })
  outputOptions(output, 'fileUploaded', suspendWhenHidden=FALSE)
  
  
  data <- reactive({
    if (is.null(input$file)) {
      return(NULL)
    }
    
    else {
      df3 <- read_excel(input$file$datapath,sheetnames()[1])
      validate(need(all(c('date1', 'date2') %in% colnames(df3)), "Incorrect file"))
      df4 <- df3 %>% mutate_if(~inherits(., what = "POSIXct"), as.Date)
      return(df4)
    }
  })
  
  data2 <- reactive({
    req(input$file)
    df1 <- read_excel(input$file$datapath,sheetnames()[2])
    df1
  })
  
  sheetnames <- eventReactive(input$file, {
    available_sheets = openxlsx::getSheetNames(input$file$datapath)
  }) 
  
 
  output$daterange <- renderUI({
    req(data())
    dateRangeInput("daterange1", "Period you want to see:",
                   start = min(data()$date2),
                   end   = max(data()$date2))
  })
  
  data_subset <- reactive({
    req(input$daterange1)
    days <- seq(input$daterange1[1], input$daterange1[2], by = 'day')
    subset(data(), date2 %in% days)
  })
  
  output$table <- renderDataTable({
    data_subset()
  })
  
}

shinyApp(ui = ui, server = server)

relating back to the 'old' thread. this data()$date2 should be otherdata()$date2 once you have written the code for populating otherdata correctly

1 Like

Thanks for reply @nirgrahamuk! One thing: It's not otherdata but eitherdata, therefore did it like this:

  output$daterange <- renderUI({
    req(eitherdata$data)
    dateRangeInput("daterange1", "Period you want to see:",
                   start = min(eitherdata()$date2),
                   end   = max(eitherdata()$date2))
  })

However, when I load the excel file via fileInput, for example, it gives the following error: object of type 'closure' is not subsettable What is wrong?

eitherdata is not accessed by $ as its a reactive function, so the req statement is malformed

req(eitherdata())
1 Like

It worked for Excel now, thanks @nirgrahamuk , but I still couldn't show daterange when I press the database button, I see that the files are loaded, but daterange is not displayed, maybe it's because in ui I have: conditionalPanel(condition = "output.fileUploaded == true", uiOutput("daterange")?

yes, why make the uiOutput conditional on a file having been uploaded ? make it conditional on eitherdata() being populated , or perhaps always place the uiOutput, but make its renderFunction conditional (req) on eitherdata() presence. i.e. make your ui logic simpler and put the 'complexity' in the server code only.

I prefer using conditionalPanel (or update* functions) over renderUI (or render* functions in general) as UI solutions usually are updated faster on the client's browser.

We could change the condition to e.g. :
conditionalPanel(condition = "output.fileUploaded == true || input.button == 'database'", [...])

Still you are right - the structure of the code can be improved.

1 Like

maybe can use a wrapper so that a logical test on the presence of eitherdata() can be done in conditionalPanel , something like this

output$either_data_populated <- renderText({
   if(isTruthy(eitherdata())){"true"} else{"false"}
})
outputOptions(output, "either_data_populated ", suspendWhenHidden=FALSE)

# then in UI
conditionalPanel(condition = "output.either_data_populated == true", uiOutput("daterange")
1 Like

Thanks for the answers @nirgrahamuk and @ismirsehregal , I tweaked this part as per @ismirsehregal suggestion as I found it easier. However, I'm still not able to access daterange when I select database, it's very weird. I know you can't access the database properly, due to the privacy of the database data which I cannot pass, but I can say that the database files are similar to the excel file I left on the link, the only difference is that it has more lines, that is, more information. When I press the database button, the files are loaded, but nothing happens (see the image below). I'll insert the entire code below in case you can see anything wrong. Am I missing something? Thank you again!

library(shiny)
library(dplyr)
library(shinythemes)

ui <- fluidPage(
  
  shiny::navbarPage(theme = shinytheme("flatly"), collapsible = TRUE,
                    br(),
                    tabPanel("PAGE1",
                             sidebarLayout(
                               sidebarPanel(
                                 radioButtons("button", 
                                              label = h3("Data source"),
                                              choices = list("Excel" = "Excel",
                                                             "Database" = "database"), 
                                              selected = "File"),
                                 br(),br(),
                                 uiOutput('fileInput'),
                                 
                                 conditionalPanel(
                                   condition = "output.fileUploaded == true || input.button == 'database'",
                                   uiOutput("daterange")
                                   
                                 )),
                               mainPanel(
                                 dataTableOutput('table')
                               )))))



server <- function(input, output) {
  
  eitherdata <- reactiveVal(NULL)
  observeEvent(data(),  eitherdata(data()))
  observeEvent(data2(),  eitherdata(data2()))
  
  output$fileInput <- renderUI({
    ib <-  req(input$button) 
    if(ib=="Excel"){
      fileInput("file",h4("Import file"), multiple = T, accept = ".xlsx")
      
    } else {
      NULL
    }
    
  })
  
  observe({
    req(input$button)  
    if (input$button =="database") {
      
    con <- DBI::dbConnect(odbc::odbc(),
                            Driver   = "[your driver's name]",
                            Server   = "[your server's path]",
                            Database = "[your database's name]",
                            UID      = rstudioapi::askForPassword("Database user"),
                            PWD      = rstudioapi::askForPassword("Database password"),
                            Port     = 1433)
      
      data <-tbl(con, in_schema("dbo", "First")) %>%
        collect()
      
      data2 <- tbl(con, in_schema("dbo", "Second")) %>% 
        collect()
           
    }
  })
  
  
  output$fileUploaded <- reactive({
    return(!is.null(data()))
  })
  outputOptions(output, 'fileUploaded', suspendWhenHidden=FALSE)
  
  
  data <- reactive({
    if (is.null(input$file)) {
      return(NULL)
    }
    
    else {
      df3 <- read_excel(input$file$datapath,sheetnames()[1])
      validate(need(all(c('date1', 'date2') %in% colnames(df3)), "Incorrect file"))
      df4 <- df3 %>% mutate_if(~inherits(., what = "POSIXct"), as.Date)
      return(df4)
    }
  })
  
  data2 <- reactive({
    req(input$file)
    df1 <- read_excel(input$file$datapath,sheetnames()[2])
    df1
  })
  
  sheetnames <- eventReactive(input$file, {
    available_sheets = openxlsx::getSheetNames(input$file$datapath)
  }) 
  
  
  output$daterange <- renderUI({
    req(eitherdata())
    dateRangeInput("daterange1", "Period you want to see:",
                   start = min(eitherdata()$date2),
                   end   = max(eitherdata()$date2))
  })
  
  data_subset <- reactive({
    req(input$daterange1)
    days <- seq(input$daterange1[1], input$daterange1[2], by = 'day')
    subset(data(), date2 %in% days)
  })
  
  output$table <- renderDataTable({
    data_subset()
  })
  
}

shinyApp(ui = ui, server = server)

Do you have 4 data sources ? seem like you read excel twice in data and data2 reactive s and you have other code that does not populate reactive when you go database.
I guess when I proposed eitherdata in the way I did I assumed data would be excel and data2 would be database (or vice versa as it doesn't matter) but it seems you have a quite different setup