-->

Need help in an algorithm for Shiny dynamic UI wit

2019-07-19 01:03发布

问题:

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

  • Asks user to upload a file
  • Gives dropdown field with options "None","Country","State","City"
  • When "None" is selected, only textoutput should appear
  • When "Country" is selected, only country filter should appear
  • When "State" is selected, both country and state filters should appear
  • when "City" is selected, both country and city filters should appear and any existing state filter from previous selections should disappear

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:

  • Country filter input need to be duplicated in the code ("State" should give both country and state filters, similarly for "City") but we cannot duplicate inputs with same ID in shiny code

  • I cannot use different country filter IDs to provide the same input because I need the input values in country filter to be read in multiple places and I would need to duplicate all those and that would complicate everything

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)

回答1:

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

Notes:

  • I used req() in data_attr as this is checks for file_attr. You don't have to use is.null().

  • I used three conditionalPanel for Country, State, City, and specified the conditions for each one.

  • Inside the condition, input.select is used not input$select.


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)