0

I need to update/reverse two inputs from drop down inputs upon a button press. At the moment when I hit the swap button (reverse_xz), it reacts however the updatePickerInput doesn't switch my x and z inputs.

I wanted to have the functionality where, once the swap button is clicked, switch the already selected pickerInputs. Then, all the drop down choices (including the selected) need to get reversed. The reason we have to remove the selected choices from vector is to prevent duplicate selections in both x and z inputs.

I am not sure if I have to render the pickerInput ui on the server side?!

This is my code below:


#global.R
library(shiny)
library(shinydashboard)
library(shinydashboardPlus)
library(shinyWidgets)
library(shinyjs)

#variable labels
my_vars <- c("None"= "NONE",
             "All" = "all_all",
             "Pro" = "Pro_",
             "Locomania" = "locomania_Type",
             "Racer" = "race")

#ui.R
ui <- shinydashboardPlus::dashboardPage(
  
  header = shinydashboardPlus::dashboardHeader( ),
  
  body = shinydashboard::dashboardBody(  box(textOutput("inputs") ) ),
  
  sidebar = shinydashboardPlus::dashboardSidebar(
    
  
    shinyWidgets::pickerInput(
      inputId = "xvar",
      label = "X Axis: ", 
      choices = my_vars,
      options = list(
        size = 5),
      multiple = FALSE,
      selected = "all_all"
    ),
    
    # Button to reverse the choices
    
    shiny::fluidRow(
      shiny::column(12, offset = 4,
                    shinyWidgets::actionBttn(
                      inputId = "reverse_xz",
                      label = "", 
                      style = "simple",
                      color = "primary",
                      icon = icon("retweet")
                    )
      )
    ),
    
    
    shinyWidgets::pickerInput(
      inputId = "zvar",
      label = "Z Axis: ", 
      choices = my_vars,
      options = list(
        size = 5),
      multiple = FALSE,
      selected = "race"
    )
    
  )
)

#server.R
server <- function(input, output, session) {
  
  # 
  observe({
    
    if(!is.null(input$reverse_xz))
      
      shinyWidgets::updatePickerInput(session, "zvar", 
                                      choices = my_vars[!(my_vars %in% input$xvar)], 
                                      selected = isolate(input$zvar) )
    
    shinyWidgets::updatePickerInput(session, "xvar", 
                                    choices = my_vars[!(my_vars %in% input$zvar)], 
                                    selected = isolate(input$xvar) )
  })
  
  
  # These observers remove the selected choices so both pickers are unique
  observe({
    
    if(!is.null(input$zvar))
      
      shinyWidgets::updatePickerInput(session, "xvar", 
                                      choices = my_vars[!(my_vars %in% input$zvar)], 
                                      selected = isolate(input$xvar) )
  })
  
  observe({
    if(!is.null(input$xvar))
      
      shinyWidgets::updatePickerInput(session, "zvar", 
                                      choices = my_vars[!(my_vars %in% input$xvar)], 
                                      selected = isolate(input$zvar) )
  })
  
  
  # output inputs
  output$inputs <- renderText({ paste0("x var: ", input$xvar,
                                       "\n\n\n z var:", input$zvar,
                                     "\n\n\nreverse press: ", input$reverse_xz) })
  
}


shiny::shinyApp(ui= ui, server= server)

enter image description here

Thank you in advance. I have looked at some relavant posts however they couldn't guide me much:

  1. Updatepickerinput with change in pickerinput in Shiny
  2. updatePickerInput not updating values after changing tabs in R shiny
  3. update pickerInput by using updatePickerInput in shiny
AOE_player
  • 536
  • 2
  • 11
  • 1
    Are the `observe` blocks even firing? I would guess you need `observe(input$reverse_xz, {...})` – geoff Jan 04 '22 at 13:38
  • Hi geoff, thank you for your help! I did try with `observeEvent(input$reverse_xz, {...})` and it acted but didn't do much. When doing what you recommend, I get `Warning: Error in : Can't access reactive value 'reverse_xz' outside of reactive consumer. i Do you need to wrap inside reactive() or observe()? 58: ` and the app crashes. – AOE_player Jan 04 '22 at 13:45
  • 1
    I have a question about what you want. Is it true that you want to: (1) switch already selected options between pickerInputs? (2) Limit the options, so if option A is selected in pickerInput X, then option A would not be displayed in pickerInput Z? Because I'm thinking about this from an hour and think that it is not possible to have both as it will lead to infinite loop. – gss Jan 04 '22 at 15:08
  • Hi @gss- Apologies, I didn't intend to waste anybody's time and energy. I should have been more clear. (1) Yes, once the swap button is clicked, switch the already selected pickerInputs. (2) Yes, all the drop down choices (including the selected) need to get reversed. The reason we have to remove the selected choices from vector is to prevent duplicate selections in both x and z inputs. I thought we can accomplish that wiht `my_vars[!(my_vars %in% input$...)]` ?! I didn't foresee it would make infinite loop with updating it. I would welcome any suggestion or alternative implementations. – AOE_player Jan 04 '22 at 15:40
  • 1
    No, that's fine. When I wrote about "an hour" I didn't mean anything negative :) – gss Jan 05 '22 at 08:44

1 Answers1

1

Look at this and check if it would be OK for you:

#global.R
library(shiny)
library(shinydashboard)
library(shinydashboardPlus)
library(shinyWidgets)
library(shinyjs)

#variable labels
my_vars <- c("None"= "NONE",
             "All" = "all_all",
             "Pro" = "Pro_",
             "Locomania" = "locomania_Type",
             "Racer" = "race")

#ui.R
ui <- shinydashboardPlus::dashboardPage(
  
  header = shinydashboardPlus::dashboardHeader( ),
  
  body = shinydashboard::dashboardBody(  box(textOutput("inputs") ) ),
  
  sidebar = shinydashboardPlus::dashboardSidebar(
    
    
    shinyWidgets::pickerInput(
      inputId = "xvar",
      label = "X Axis: ", 
      choices = my_vars,
      options = list(
        size = 5),
      multiple = FALSE,
      selected = "all_all"
    ),
    
    # Button to reverse the choices
    
    shiny::fluidRow(
      shiny::column(12, offset = 4,
                    shinyWidgets::actionBttn(
                      inputId = "reverse_xz",
                      label = "", 
                      style = "simple",
                      color = "primary",
                      icon = icon("retweet")
                    )
      )
    ),
    
    
    shinyWidgets::pickerInput(
      inputId = "zvar",
      label = "Z Axis: ", 
      choices = my_vars,
      options = list(
        size = 5),
      multiple = FALSE,
      selected = "race"
    )
    
  )
)

#server.R
server <- function(input, output, session) {
  
  # 
  observeEvent(input$reverse_xz, {
    
    shinyWidgets::updatePickerInput(session, "zvar", 
                                    choices = my_vars[!(my_vars %in% input$zvar)], 
                                    selected = input$xvar)
    
    shinyWidgets::updatePickerInput(session, "xvar", 
                                    choices = my_vars[!(my_vars %in% input$xvar)], 
                                    selected = input$zvar)
  })
  
  observe({
    
    if (input$xvar == input$zvar && (length(input$zvar) > 0 && length(input$xvar) > 0)) {
      shinyWidgets::updatePickerInput(session, "zvar", 
                                      selected = "")
      
      shinyWidgets::updatePickerInput(session, "xvar", 
                                      selected = "")
      
    }
    
  })
  
  # output inputs
  output$inputs <- renderText({ paste0("x var: ", input$xvar,
                                       "\n\n\n z var:", input$zvar,
                                       "\n\n\nreverse press: ", input$reverse_xz) })
  
}

shiny::shinyApp(ui= ui, server= server)

I think that maybe this needs an explanation:

if (input$xvar == input$zvar && (length(input$zvar) > 0 && length(input$xvar) > 0))

So, when user choose two the same inputs, then we are updating pickerInputs, so both will have "Nothing selected" as a sign for user that something goes wrong (or that she/he did something wrong). However, "Nothing selected" is like NULL and we can't use NULL like this NULL == "something" inside if, so I'm checking if some input is NULL using length(input$) > 0, because length of NULL is 0. Instead of length(input$) > 0 you could use !is.null(input$) and maybe you should as it is probably more readable, but I'm leaving this decision for you.

gss
  • 1,334
  • 6
  • 11