SRL
SRL

Reputation: 167

Preserve ordering of selectizeInput selections in R Shiny

Steps to replicate my problem:

  1. Run app
  2. Select "D" in Choice 2
  3. Remove "C" in Choice 1
  4. In Choice 1, drag "B" in front of "A"

Choice 1 has pre-selected options, while Choice 2 is blank by default.

When a selection is made in Choice 1, that option is removed in the drop down list for Choice 2.

I would like to be able to order my selections, but they always go back to the original order. How do I prevent this from happening?

library(shiny)
library(flexdashboard)

# UI ----------
ui <- fluidPage(
        uiOutput("Select1"),
        uiOutput("Select2"))

# Server -------------
server <- function(input, output, session) {
  vals <- reactiveValues(sel1 = character(),
                        sel2 = character())

observeEvent(input$Select1, {
    vals$sel1 = input$Select1
})

observeEvent(input$Select2, {
     vals$sel2 = input$Select2
})

  output$Select1 <- renderUI({

    letters = c("A", "B", "C", "D", "E", "F", "G")
    selected = c("A", "B", "C")

    if (length(vals$sel1) != 0 && vals$sel1 %in% letters) {
      selected = vals$sel1
    }

    selectizeInput('Select1', label = "Choice 1:",
                   multiple = TRUE,
                   choices = letters[!letters %in% input$Select2],
                   selected = selected,
                   options = list(plugins = list('remove_button', 'drag_drop')))

  })

  output$Select2 <- renderUI({

    letters = c("A", "B", "C", "D", "E", "F", "G")
    selected <- NULL

    if (length(vals$sel2) != 0 && vals$sel2 %in% letters) {
      selected = vals$sel2
    }

    selectizeInput('Select2', label = "choice 2:",
                   multiple = TRUE,
                   choices = letters[!letters %in% input$Select1],
                   selected = selected,
                   options = list(plugins = list('remove_button', 'drag_drop')))

  })
}

shinyApp(ui = ui, server = server)

Using isolate() around selected doesn't work. I was able to get the ordering to work with the following code, but then lost the ability to preserve my selections.

observeEvent(input$Select1, {
  # Allows reordering of columns
  if (!all(vals$sel1 %in% input$Select1)) {
    vals$sel1 = input$Select1
  }
})

observeEvent(input$Select2, {
  # Allows reordering of columns
  if (!all(vals$sel2 %in% input$Select2)) {
    vals$sel2 = input$Select2
  }
})

Can anyone help?

Upvotes: 1

Views: 946

Answers (2)

YBS
YBS

Reputation: 21349

Perhaps orderInput() from shinyjqui package might be useful in your case. Please note that if in your use case you have too many choices in select3, you may need to reconsider. Try this

library(shiny)
library(shinyjqui)

### UI ----------
ui <- fluidPage(

  orderInput(inputId = 'select1', 'Choice 1: ', items = LETTERS[1:3], connect = c("select3", "select2")),
  orderInput(inputId = 'select2', 'Choice 2: ', items = NULL, connect = c("select1", "select3"), placeholder = 'Drag item here...'),
  orderInput(inputId = 'select3', 'Choose items', items = LETTERS[4:7], connect = c("select1", "select2")),
  verbatimTextOutput("t1")

)

server <- function(input, output) {
  output$t1 <- renderPrint({input$select1})
}

shinyApp(ui, server)

output

Upvotes: 1

Marcus
Marcus

Reputation: 3646

Basically, you are recreating the UI element every time the input selects which causes the selections to be sorted. I personally would probably consider using updateSelectizeInput because rendering them dynamically on the server side sort of undermines the point of selectize.

That being said, you can add a req statement to your code to prevent them from the dynamic UI from re-rendering unless the values are actually changed (or there are none to cover initial rendering)

  output$Select1 <- renderUI({
    
    req(!all(vals$sel1 %in% input$Select1) | is.null(input$Select1), cancelOutput = TRUE)
    
    letters = c("A", "B", "C", "D", "E", "F", "G")
     ...<<other code>>...
    
  })
  
  output$Select2 <- renderUI({
    
    req(!all(vals$sel2 %in% input$Select2) | is.null(input$Select1), cancelOutput = TRUE)
    
    letters = c("A", "B", "C", "D", "E", "F", "G")
    
    ...<<other code>>...
    
  })

Upvotes: 2

Related Questions