shinydynamic-ui

Need help in an algorithm for Shiny dynamic UI with multiple interactions


My Shiny Algorithm (app.R code at the bottom):

What I did in the code: Used IF condition for "None" and "Country"; used Switch for "State" and "City".

My Problem: Everything is working as expected except one: If we select "State" after selecting "None", I am seeing both textOutput and State filter instead of country and state filters. This happens because the IF statement in "None" or "Country", unlike switch(), just prints the UI and wouldn't clear the UI if another selection is made

My Constraint:

Where I need help: My only issue here is, I need both "Country" and ("State"/"City") filter to appear if a user selects ("State"/"City") after "None" (bypassing "Country"!!!).

Really need this thing to get fixed. Any help would be appreciated

Thanks!

Dummy Data: https://docs.google.com/spreadsheets/d/1E9dbtOMm1-7ZjIHu3Ra_NyFBHrCQdITG2_xuMIMKDOs/edit?usp=sharing

app.R code

library(shiny)

ui <- fluidPage(

  fileInput("file_attr", "Door attributes:"),
  selectInput("select", label = "Region Drop down", choices = list("None", "Country", "State","City"), selected = "None"),
  uiOutput("NoneCountryFilter"),
  uiOutput("StateCityFilter")
)

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

  #Reading input
  data_attr <- reactive({
    file1 <- input$file_attr
    if(is.null(file1)){return()} 
    read.table(file=file1$datapath, sep=",", header = TRUE, stringsAsFactors = FALSE)
  })

  #Filter interactivity

  #Reading Lists
  countries <- reactive({
    if(is.null(data_attr()$Country)){return()}
    data_attr()$Country
  })

  states <- reactive({
    if(is.null(data_attr()$State)){return()}
    data_attr()$State[data_attr()$Country %in% input$show_vars]
  })

  cities <- reactive({
    if(is.null(data_attr()$City)){return()}
    data_attr()$City[data_attr()$Country %in% input$show_vars]
  })


  #Filters based on Region Drop down

  observeEvent(input$file_attr,{ 
    observe({

      if ("None" %in% input$select){
        output$NoneCountryFilter <- renderUI({
          if(is.null(data_attr()$Country)){return()}
          h4("No region Selected")              
        })        
      }

      if ("Country" %in% input$select){
        output$NoneCountryFilter <- renderUI({
          if(is.null(data_attr()$Country)){return()}
          selectizeInput('show_vars', 'Country Filter', choices = c("Select All","None", unique(countries())), multiple = TRUE)
        })    
      }        
    })

    output$StateCityFilter <- renderUI({

      switch(input$select,                
             "State" = (
               selectizeInput('show_vars_state', 'State Filter', choices = c("Select All","None", unique(states())), multiple = TRUE)
             ),
             "City" = (
               selectizeInput('show_vars_city', 'City Filter', choices = c("Select All","None", unique(cities())), multiple = TRUE)
             )         
      )         
    })       
  })

  #Giving "Select ALL" and "None" Functionality to each filter (This part is redundant for the current problem. I am keeping this so that I could check any solution from stackoverflow should not effect other functionalities)

  #Countries- SelectAll & None
  observe({
    if ("Select All" %in% input$show_vars){
      selected_choices <- setdiff(c("Select All",unique(countries())), "Select All")
      updateSelectizeInput(session, 'show_vars', choices = c("Select All","None",unique(countries())), selected = selected_choices) 
    }
  })  


  observe({
    if ("None" %in% input$show_vars){
      updateSelectizeInput(session, 'show_vars', choices = c("Select All", "None", unique(countries())),selected = "")
    }
  })


  #State- SelectAll & None

  observe({
    if ("Select All" %in% input$show_vars_state){
      selected_choices <- setdiff(c("Select All",unique(states())), "Select All")

      updateSelectizeInput(session, 'show_vars_state', choices = c("Select All","None",unique(states())), selected = selected_choices)

    }
  })

  observe({
    if ("None" %in% input$show_vars_state){
      updateSelectizeInput(session, 'show_vars_state', choices = c("Select All", "None", unique(states())),selected = "")
    }
  })        

  #City- SelectAll & None

  observe({

    if ("Select All" %in% input$show_vars_city){

      selected_choices <- setdiff(c("Select All",unique(cities())), "Select All")

      updateSelectizeInput(session, 'show_vars_city', choices = c("Select All", "None", unique(cities())), selected = selected_choices)

    }
  })

  observe({

    if ("None" %in% input$show_vars_city){
      updateSelectizeInput(session, 'show_vars_city', choices = c("Select All", "None", unique(cities())),selected = "")
    }
  })    
}

shinyApp(ui = ui, server = server)

Solution

  • You could use conditionalPanel to determine which elements to be displayed based on certain conditions.

    Notes:


    library(shiny)
    
    ui <- fluidPage(
    
      fileInput("file_attr", "Door attributes:"),
      selectInput("select", label = "Region Drop down",
                  choices = list("None", "Country", "State","City"), selected = "None"),
    
      # Selectize Inputs ---------------
      uiOutput("Country_List"),
      uiOutput("State_List"),
      uiOutput("City_List")
    )
    
    server <- function(input, output, session) {
    
      #Reading input
      data_attr <- reactive({
        req(input$file_attr) # make sure a file was uploaded
    
        read.table(file=input$file_attr$datapath, sep=",", header = TRUE, stringsAsFactors = FALSE)
      })
    
      # Country selectizeInput ----------------------
      output$Country_List <- renderUI({
        conditionalPanel(
          condition = "input.select == 'Country' | input.select == 'State'| input.select == 'City' ",
          selectizeInput('show_var', 'Country Filter',
                         choices = c("Select All","None", unique(data_attr()$Country)), multiple = TRUE)
        )
      })
    
      # State selectizeInput ----------------------
      output$State_List <- renderUI({
        conditionalPanel(
          condition = "input.select == 'State' ",
          selectizeInput('show_vars_state', 'State Filter',
                         choices = c("Select All","None", unique(data_attr()$State)), multiple = TRUE)
        )
      })
    
      # City selectizeInput -----------------------
      output$City_List <- renderUI({
        conditionalPanel(
          condition = "input.select == 'City' ",
          selectizeInput('show_vars_city', 'City Filter',
                         choices = c("Select All","None", unique(data_attr()$City)), multiple = TRUE)
        )
      })
    
    }
    
    shinyApp(ui = ui, server = server)