2

Is it possible in shiny to reset radioButtons to an empty value (i.e. as if the radiobuttons would not have been clicked and the default value was empty (selected = ""))?

I tried reset("btn") with shinyjs() and updateRadioButtons(session, "btn",...). However, I only managed to reset the radiobuttons to a specific value (e.g. the first value in the list or another predefined value), but not to an empty value.

The reset to an empty value would make it possible to use a single set of radiobuttons for many repeated choices in a stated choice experiment (in combination with renderUI, where the radiobuttons are successively assinged to different variables.

4 Answers4

3

It's generally agreed upon (though of course nothing opinion-based is 100%) that radio buttons should not be used if you want the user to be able to revert back to the original no selection state. Even the documentation for radio buttons in shiny says this:

If you need to represent a "None selected" state, it's possible to default the radio buttons to have no options selected by using selected = character(0). However, this is not recommended, as it gives the user no way to return to that state once they've made a selection. Instead, consider having the first of your choices be c("None selected" = "").

If you look up discussions online you'll also see that it's commonly argued that a radio button with no initial selection is ok, with the caveat that often it means the user cannot go back to that state after making a selection because de-selecting is not really natively supported by radio buttons.

UPDATE

An update using updateRadioButtons() may appear to work visually, but it doesn't actually reset the value to be unselected. The underlying value of the input is unchanged. Here's proof:

library(shiny)

ui <- fluidPage(
  radioButtons("btn","Click Me",choices  = c("Choice 1","Choice s"),selected = character(0)),
  actionButton("clickMe","Click Me to Reset"),
  actionButton("showValue", "Show value")
)

server <- function(input, output, session) {
  observeEvent(input$clickMe,{
    updateRadioButtons(session,"btn",choices  = c("Choice 1","Choice s"),selected = character(0))
  })

  observeEvent(input$showValue, {
    print(input$btn)
  })
}

shinyApp(ui, server)
DeanAttali
  • 25,268
  • 10
  • 92
  • 118
0

Agree with @Dean Attali. But if it is absolutely needed (Not Recommended),

library(shiny)

ui <- fluidPage(
  radioButtons("btn","Click Me",choices  = c("Choice 1","Choice s"),selected = character(0)),
  actionButton("clickMe","Click Me to Reset")
)

server <- function(input, output, session) {
  observeEvent(input$clickMe,{
    updateRadioButtons(session,"btn",choices  = c("Choice 1","Choice s"),selected = character(0))
  })
}

shinyApp(ui, server)

Consider using a checkbox gorup as an alternative?

Edit

library(shiny)

ui <- fluidPage(
  radioButtons("btn","Click Me",choices  = c("Choice 1","Choice s"),selected = character(0)),
  actionButton("clickMe","Click Me to Reset"),
  actionButton("showValue", "Show value")
)

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

  selected = reactiveVal(NULL)

  observeEvent(input$clickMe,{
    updateRadioButtons(session,"btn",choices  = c("Choice 1","Choice s"),selected = character(0))
    selected(NULL)
  })

  observeEvent(input$btn,{
    selected(input$btn)
  })


  observeEvent(input$showValue, {
    print(selected())
  })
}

shinyApp(ui, server)

you can use a reactive val as a proxy. But we are getting into real hacky territory here.

Edit 2 Custom javascript solution

Here is the custom javascript solution discussed in the comments. In this solution if the user double clicks the radio button it gets unselected. You can double check the value though the button. Note you will not refer to input$btn for the button value. A custom value is created with input$radio_click.

library(shiny)

ui <- fluidPage(
  tags$script(HTML("
              window.onload = function(){
                 var btns = document.getElementById('btn');
                 btns_radio = btns.getElementsByTagName('input');

                 for(var i = 0; i < btns_radio.length; i++){
                   btns_radio[i].addEventListener('click',function(x){
                      Shiny.onInputChange('radio_click', this.value)
                   });

                   btns_radio[i].addEventListener('dblclick',function(x){
                      if(this.checked){
                        this.checked = false;
                        Shiny.onInputChange('radio_click', null)
                      }
                   })
                   }
                 }
              ")),
  radioButtons("btn","Click Me",choices  = c("Choice 1","Choice s"),selected = character(0)),
  actionButton("showValue", "Show value")
)

server <- function(input, output, session) {
  observeEvent(input$showValue, {
    print(input$radio_click)
  })
}

shinyApp(ui, server)
Sada93
  • 2,785
  • 1
  • 10
  • 21
  • I tried selected = character(0) but it did not solve the problem. The radiobutton looks empty, but the variable input$btn remains with the value of the latest choice, such that all upstream choices receive by default the value of the latest choice. – Reinhard Hoessinger Feb 19 '19 at 20:20
  • 1
    This doesn't actually change the value of the input (it only makes the visual appearance seem like it's unchecked). See the update on my answer for proof – DeanAttali Feb 19 '19 at 20:20
  • @DeanAttali interesting. Yeah a "None Selected" option seems to be the only way to go here. – Sada93 Feb 19 '19 at 20:57
  • Is it possible to reset the value of the underlying input variable as well? I tried updateNumericInput(session, "btn", value = "") but id had no effect. The 'non selected' option works but is ugly. Choice experiments are highly standardised and it is unusual (and rather confusing) to provide a non selected option. – Reinhard Hoessinger Feb 19 '19 at 21:00
  • @ReinhardHoessinger updated the answer by using a reactive Val as a proxy. It does start to get Hacky. – Sada93 Feb 19 '19 at 21:13
  • The reactiveVal solution would work. Alternatively I'm fairly certain you could write some custom javascript to set the value to nothing if you were inclined to experiment with that. – DeanAttali Feb 19 '19 at 22:26
  • @DeanAttali yeah i started trying this with Shiny.onInputChange. It isnt trivial haha. – Sada93 Feb 19 '19 at 22:27
  • @Dean Attali added the custom JS solution. – Sada93 Feb 20 '19 at 01:31
  • 1
    I was thinking more along the lines of using javascript to uncheck the radio button and set the input's value to empty :) Unfortunately there is just no elegant solution! – DeanAttali Feb 20 '19 at 02:09
  • Finally I could solve the problem thanks to the fact that, in stated choice experiments, respondents are forced to make a choice in each repeated task. So I introduced a lock variable, which allows only one step to the next choice task (clicking the next button) and then locks the next button (shows a warning when the next button is clicked) until the respondent has clicked a radio button. Maybe not the most elegant solution but it does exactly what I want. – Reinhard Hoessinger Feb 20 '19 at 16:00
  • Meanwhile I also checked the javascript solution from the 2nd edit. It works very well indeed (even without shinyjs). This is the elegant solution, thank you. It was the missing piece for using a single set of radiobuttons for repeated choice experiments. – Reinhard Hoessinger Feb 20 '19 at 19:18
0

@DeanAttali has the right idea.

resetRadioGroup = (formName, radioGroupName) => {
    let form = document.forms[formName],
        radioGroup = form && form.elements[radioGroupName],
        selectedValue = radioGroup && radioGroup.value,
        selectedRadio = form.querySelector(`[value=${selectedValue}]`);

    if(selectedRadio){
        selectedRadio.checked = undefined;
    }
}
cage rattler
  • 1,587
  • 10
  • 16
0

An alternative could be generating a new id for radioButtons() each time renderUI() generates a new radioButtons(). This way you always get a NULL value in the beginning.

shinyServer(function(input, output, session) {
  radio_id <- "first_time"
  output$test <- renderUI({
    radio_id <<- paste0("but", gsub("\\s+|:", "", Sys.time()))
  
    radioButtons(
        inputId = radio_id,
        label = "Choose the correct answer",
        inline = TRUE,
        choices = t_item$choices, 
        selected = character(0)
    )
  })
})
slamballais
  • 3,161
  • 3
  • 18
  • 29
Edgar Manukyan
  • 1,153
  • 10
  • 21