Siemkowski
Siemkowski

Reputation: 1431

if statement get rid of: Error in if: argument is of length zero

The idea

Depending on selectizeInput() input I want to display one row of data from a data frame. However, some variables are only to be displayed if their value is not FALSE. selectizeInput() includes a placeholder prompt, which is highly desirable.

The problem

The code I developed actually works but until any option is selected displays

Error in if: argument is of length zero

inside the app, which is highly undesirable.

Example code

library("shiny")

# simulate data
data <- data.frame(
  name = c("Adam", "Debbie", "Lancelot", "Dracula"),
  age = c(21, 48, 72, 1023),
  coward = c("yes, a coward", "just a little bit", FALSE, "too old to be a coward")
)

ui <- fluidPage(
  # selectize
  selectizeInput(
    inputId = "input",
    label = NULL,
    choices = c("..." = "", as.character(data[, "name"])),
    selected = NULL,
    multiple = FALSE
  ),
  # show output
  uiOutput(outputId = "output")
)

server <- function(input, output, session){
  output$output <- renderUI({
    div(
      # show name
      data[which(input$input == data[, "name"]), "name"], br(),
      data[which(input$input == data[, "name"]), "age"], br(),
      # if "coward" is not FALSE than show its value
      if(data[which(input$input == data[, "name"]), "coward"] != FALSE){
        data[which(input$input == data[, "name"]), "coward"]
      }
    )
  })
}

shinyApp(ui, server)

Attempted solutions

Tried wrapping if() statement in suppressWarnings() or suppressMessages(). Does not change anything. Also tried putting !is.null(input$input) && inside if() condition but it just changes the initial error to

Error: missing value where TRUE/FALSE needed

The question

How to write the condition so that shiny would not return an error before selecting an option in selectizeInput()?

Any other, more general hack to force shiny not to display error messages inside the app?

I feel I`m missing something obvious here...

Upvotes: 4

Views: 12803

Answers (3)

Siemkowski
Siemkowski

Reputation: 1431

The answer including browser() function was indeed helpful for locating the source of the problem but the real solution is to use req() or a combination of validate() and need() functions. See respective help() pages to learn how to use them.

In short what these functions do is to check whether the argument given has been specified. In reference to the example code the solution could look like this:

server <- function(input, output, session){
  output$output <- renderUI({
    # check if input$input is specified
    # if not stop executing the rest of this code block
    req(input$input)
    div(
      # show name
      data[which(input$input == data[, "name"]), "name"], br(),
      data[which(input$input == data[, "name"]), "age"], br(),
      # if "coward" is not FALSE than show its value
      if(data[which(input$input == data[, "name"]), "coward"] != FALSE){
        data[which(input$input == data[, "name"]), "coward"]
      }
    )
  })
}

Upvotes: 8

DeanAttali
DeanAttali

Reputation: 26313

Whenever you get an error you're not sure about in R, the first thing to do is to try to isolate it and use browser() to see what happens just before the error. After doing this, I found out the error has nothing to do with shiny or the UI, it's just because you're trying to compare a factor(0) to FALSE.

The value of input$input is initially "". This means the value of data[which(input$input == data[, "name"]), "coward"] is initially factor(0). Which means your if statement is doing a factor(0) == FALSE comparison, which breaks.

To figure that out all I needed to do was simplify the server code to

server <- function(input, output, session){
  observe({
    mydata <- data[which(input$input == data[, "name"]), "coward"]
    browser()
    if (mydata == FALSE) {
      cat("hello")
    }
  })
}

And using the debugger I saw what values are being used

Upvotes: 8

Sandipan Dey
Sandipan Dey

Reputation: 23101

A hack that works:

server <- function(input, output, session){
  output$output <- renderUI({
    div(
      # show name
      data[which(input$input == data[, "name"]), "name"], br(),
      data[which(input$input == data[, "name"]), "age"], br(),
      # if "coward" is not FALSE than show its value
      if((length(input$input) > 1) && 
         (data[which(input$input == data[, "name"]), "coward"] != FALSE)){
        data[which(input$input == data[, "name"]), "coward"]
      }
    )
  })
}

Upvotes: 0

Related Questions