2

I have two selectizeInputs (state1 and state2) in my app that have the same set of choices. When the user clicks the actionButton, I would like to compare the values of state1 and state2 and display a feedback message to the user if they are the same.

Both selectizeInputs are initialised with the placeholder "Search by name". If I change the value of one input for the first time and leave the value of the other unchanged at its placeholder value, then clicking the action button triggers the feedback message:

enter image description here enter image description here

This is puzzling to me because the message should only be displayed if state1 and state2 are the same.

Here is the code to reproduce the above:

library(shiny)
library(shinyFeedback)


ui <- fluidPage(shinyFeedback::useShinyFeedback(),
                selectizeInput('state1', 'Select state 1', choices = state.name, 
                               multiple = T, 
                               options = list(placeholder = "Search by name", maxItems = 1)),

                selectizeInput('state2', 'Select state 2', choices = state.name, 
                               multiple = T, 
                               options = list(placeholder = "Search by name", maxItems = 1)),

                actionButton("click", "Click"),

                textOutput("states")
)

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

  values = reactiveValues(states = NULL)

  observeEvent(input$click, {

    feedbackDanger("state1", input$state1 == input$state2, "")

    feedbackDanger("state2", input$state2 == input$state1, "State 1 and state 2 cannot be the same.")

    req(!is.null(input$state1), !is.null(input$state2), input$state1 != input$state2)

    values$states <- paste(input$state1, input$state2)

  })

  output$states <- renderText({

    values$states

  })
}

shinyApp(ui, server)

I think this may have something to do with using multiple = T and maxItems = 1 in the selectizeInputs or with the way I have written the trigger conditions but I don't understand why they are wrong. Any help would be greatly appreciated.

user51462
  • 1,098
  • 6
  • 20

1 Answers1

4

Edit: 2019-02-13

The bug has been resolved in the GitHub version of shinyFeeback. Your original code should work if you install the package from GitHub:

devtools::install_github("merlinoa/shinyFeedback")

--

Issue

You were thinking along the right lines! In your first example, input$state1 is "Alaska" and input$state2 is empty. The initial value of selectizeInput with multiple = T is NULL.

When your feedbackDanger condition compares the two values, the result is an empty logical:

> "Alaska" == NULL
logical(0)

The empty logical is passed as an empty object to the shinyFeedback javascript function checkFeedback. Empty objects are truthy, and so are "considered true when encountered in a Boolean context." So when checkFeedback evaluates the value:

if (message.condition) {

   ....

the statement returns true and the feedbackDanger message is displayed.

Solution

A simple solution would be to first check if either value is NULL and then check for equality, like this:

((!is.null(input$state1) & !is.null(input$state2)) && (input$state1 == input$state2))

Edit: We use && so that we evaluate if either value is NULL first. Only if neither value is NULL, do we then evaluate whether the values are the same. Evaluating in this order avoids sending the empty logical to the JS function. See this answer for more information on & vs &&. /Edit

A full working solution would look like this:

library(shiny)
library(shinyFeedback)


ui <- fluidPage(shinyFeedback::useShinyFeedback(),
                selectizeInput('state1', 'Select state 1', choices = state.name, 
                               multiple = T, 
                               options = list(placeholder = "Search by name", maxItems = 1)),

                selectizeInput('state2', 'Select state 2', choices = state.name, 
                               multiple = T, 
                               options = list(placeholder = "Search by name", maxItems = 1)),

                actionButton("click", "Click"),

                textOutput("states")
)

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

    values = reactiveValues(states = NULL)

    observeEvent(input$click, {
        condition <- ((!is.null(input$state1) & !is.null(input$state2)) && (input$state1 == input$state2))

        feedbackDanger("state1", condition, "")

        feedbackDanger("state2", condition, "State 1 and state 2 cannot be the same.")

        req(!is.null(input$state1), !is.null(input$state2), input$state1 != input$state2)

        values$states <- paste(input$state1, input$state2)

    })

    output$states <- renderText({

        values$states

    })
}

shinyApp(ui, server)
Hallie Swan
  • 2,364
  • 1
  • 9
  • 16
  • 1
    Thank you so much for your detailed explanation! I'm not familiar with "truthy" values so I really appreciate it. Would it please be possible to explain why you chose to use && instead of & for the second comparison? – user51462 Feb 07 '19 at 06:33
  • 1
    @user51462 You're welcome! Happy to help. I added an explanation of using && instead of & to the answer. Hope it makes sense! – Hallie Swan Feb 07 '19 at 17:59
  • 1
    @user51462 the bug was fixed. if you download the package directly from GitHub, your original code should work. I edited the answer to reflect this – Hallie Swan Feb 13 '19 at 17:15