insertUI/removeUI based on a checkboxInput

shiny

#1

Hi everyone,

I am trying to depend on a checkboxInput to insert and remove a UI. The program basically goes like this; if I check the box, then a file upload input will be displayed, can if I uncheck it, the fileuploadinput will be removed. One slightly complicated feature is it supports multiple checkbox input and whichever of them being checked would trigger the insert file input UI, but there should be only one file input UI on the app the whole time. When nothing is checked, the file input UI will be remove.

Here is the program I wrote; it accomplishes basically everything I described above except that the file input UI would not be removed as expected. Can someone take a look?

## Only run this example in interactive R sessions
if (interactive()) {
# Define UI
ui <- fluidPage(
  checkboxInput("indicator1", label = NULL , value = FALSE, width= NULL),
  checkboxInput("indicator2", label = NULL , value = FALSE, width= NULL),
  checkboxInput("indicator3", label = NULL , value = FALSE, width= NULL)

)

# Server logic
server <- function(input, output, session) {
  UI_exist = FALSE
  observeEvent({input$indicator1; input$indicator2; input$indicator3}, { # observe all three checkbox inputs
    if(!UI_exist && sum(input$indicator1, input$indicator2, input$indicator3) == 1){ # if no UI exists and 
                                                                                     #only one checkboxinput is checked
      insertUI(
        selector = "#indicator1",
        where = "beforeBegin",
        ui = fileInput("upload", "Upload file", multiple = FALSE),
      )  
      UI_exist <<- TRUE
    }
    
    if(UI_exist && all(!input$indicator1, !input$indicator2, !input$indicator3)){ # if UI exists and 
                                                                      # all are false, i.e. no one is checked, then remove UI    
      removeUI(selector = "div:has(> #upload)")
      UI_exist <<- FALSE
    }

  })
}

# Complete app with UI and server components
shinyApp(ui, server)
}

#2

The issue is with the selector. It does not find the element of UI you want to remove. (if you put debug printing inside your functions, you'll see the logic is correct and it enters the removeUI part.)
So you need to find a better selector for the fileinput element.

shiny::fileInput("upload", "Upload file", multiple = FALSE)
corresponds to this html code
<div class="form-group shiny-input-container">

<label>Upload file</label>

<div class="input-group">

<label class="input-group-btn">
<span class="btn btn-default btn-file">
Browse…
<input id="upload" name="upload" type="file" style="display: none;"/>
</span>
</label>
<input type="text" class="form-control" placeholder="No file selected" readonly="readonly"/>

</div>

<div id="upload_progress" class="progress progress-striped active shiny-file-input-progress">

<div class="progress-bar">

</div>

</div>

</div>

not easy to find something to select.

By the way, "div:has(> #upload)" do not work because, in the html above from fileinputUI, that you want to remove, there input tag with id #upload is not directly under a div. So it is a wrong selector in this case.


#3

One way to help is in fact to create some easier things to select with custom id.

This should work.

  • Create a div with an id to put you file input UI into
  • Insert your UI inside this div
  • use a selector to select all that is inside this div to remove, keeping an empty div to put again the fileinput UI if needed.
## Only run this example in interactive R sessions
if (interactive()) {
  # Define UI
  ui <- fluidPage(
    # create a div to place your fileinput into
    div(id = "fileinput"),
    # place you checkinput after in the same div (safety mesure)
    div(id = "checkboxinput", 
        checkboxInput("indicator1", label = NULL , value = FALSE, width= NULL),
        checkboxInput("indicator2", label = NULL , value = FALSE, width= NULL),
        checkboxInput("indicator3", label = NULL , value = FALSE, width= NULL)
    )
  )
  
  # Server logic
  server <- function(input, output, session) {
    UI_exist = FALSE
    observeEvent({input$indicator1; input$indicator2; input$indicator3}, { # observe all three checkbox inputs
      if(!UI_exist && sum(input$indicator1, input$indicator2, input$indicator3) == 1){ # if no UI exists and 
        #only one checkboxinput is checked
        insertUI(
          # insert inside the div input
          selector = "#fileinput",
          where = "afterBegin",
          ui = fileInput("upload", "Upload file", multiple = FALSE),
        )  
        UI_exist <<- TRUE
      }
      
      if(UI_exist && all(!input$indicator1, !input$indicator2, !input$indicator3)){ # if UI exists and 
        # all are false, i.e. no one is checked, then remove UI    
        # remove the div that is inside the div with id fileinput we created
        removeUI(selector = "div#fileinput > div")
        UI_exist <<- FALSE
      }
      
    })
  }
  
  # Complete app with UI and server components
  shinyApp(ui, server)
}

#4

In addition to @cderv's suggestion, you could also put the fileInput in a conditionalPanel, where the condition is something like "input.indicator1 || input.indicator2 || input.indicator3"