Is there a way to turn on (or are there plans to add) code completion for shiny input and output elements? I find that 90% of my bug troubleshooting in shiny is because I do something stupid like this:
# server code
output$box <- renderUI({
checkboxInput("check", "a checkbox")
})
variable <- reactive({
req(input$checkbox)
some_code()
})
Since R generally uses lazy evaluation for shiny elements, my reactive variable never gets calculated (or my conditional UI doesn't show up, or whatever), because it's waiting for an input variable that doesn't exist. The lazy evaluation is usually nice, but is a pain in the butt for debugging ("why isnt my stupid ___ showing up?!"). Rstudio's autocomplete doesn't give me anything after input$ and its line-by-line error checking doesn't say "no variable with that name in scope" or whatever. Similarly, output$ variables don't recognize when there's no xxOutput() object that calls them, and don't recognize when xxOutput() calls an undefined output$ object. I understand that this kind of code completion may be trickier in split ui/server apps, but I feel like it would save hours of frustration and headache.
Shiny code completions work if you use separate files ui.R and server.R instead of one app.r. Save the ui.R first and then in server.R inside the server function type input$ and wait for the code completion to assist you.. it will show all input variables available
Thanks Sanjmeh. This sortof answers my question but also leaves me deeply unsatisfied.
I split my app per your suggestion, but I found that code completion only works for input variables that are defined in ui.r. For me, this means two—out of 40+ and counting. The vast majority of my inputs are constructed on the server side, since they're either conditionals that are too complicated for conditionalPanel() or are displayed in modals (or both). Notably, the example I gave above does not work when you split the app, because input$check is defined in server.r.
So, I suppose my question still stands. Is there a way to enable or are there plans to add code completion for the example above?
I am not sure I can help in your situation. To be honest I don't understand why you are defining an input reactive inside the server. Of course you can update the input reactive from inside the server.R e.g. updateSelectInput but that is not what you are doing it seems.
Let's wait for others to advise. I am also keen to see how this works.
No, you are correct; that is not what I'm doing. For an example of when defining an input on the server side is necessary, see the below example, from the modalDialog() helptext.
It has a modal popup that requires you to make some input with textInput("dataset", ...) before continuing. Since modalDialog() is a server side operation, any inputs within modals are also defined on the server side.
# Display a modal that requires valid input before continuing.
shinyApp(
ui = basicPage(
actionButton("show", "Show modal dialog"),
verbatimTextOutput("dataInfo")
),
server = function(input, output) {
# reactiveValues object for storing current data set.
vals <- reactiveValues(data = NULL)
# Return the UI for a modal dialog with data selection input. If 'failed' is
# TRUE, then display a message that the previous value was invalid.
dataModal <- function(failed = FALSE) {
modalDialog(
textInput("dataset", "Choose data set",
placeholder = 'Try "mtcars" or "abc"'
),
span('(Try the name of a valid data object like "mtcars", ',
'then a name of a non-existent object like "abc")'),
if (failed)
div(tags$b("Invalid name of data object", style = "color: red;")),
footer = tagList(
modalButton("Cancel"),
actionButton("ok", "OK")
)
)
}
# Show modal when button is clicked.
observeEvent(input$show, {
showModal(dataModal())
})
# When OK button is pressed, attempt to load the data set. If successful,
# remove the modal. If not show another modal, but this time with a failure
# message.
observeEvent(input$ok, {
# Check that data object exists and is data frame.
if (!is.null(input$dataset) && nzchar(input$dataset) &&
exists(input$dataset) && is.data.frame(get(input$dataset))) {
vals$data <- get(input$dataset)
removeModal()
} else {
showModal(dataModal(failed = TRUE))
}
})
# Display information about selected data
output$dataInfo <- renderPrint({
if (is.null(vals$data))
"No data selected"
else
summary(vals$data)
})
}
)
}