SelectizeInput inside DT::datatable only works as html

Viewed 539

I am trying to optimize the UX for my end user in a time tracking app. Essentially it pulls in data from google calendar's api, arranges it in a tibble, and let's the user select/deselect/edit meetings, and assign them to projects. The projects are selected in a dropdown menu that I used selectizeInput to build, but am getting something similar to selectInput

I want to use a shiny::selectizeInput within a DT::datatable in Shiny. I can get the drop down to work. However, I am losing the search function that comes with selectize input. In my toy example the top selectizeInput you can click and type in the options you are looking for. The one within the datatable, you can still kind of do that, but with complicated names it would be better for the UX if you can see what you were typing.

I found this issue in the github repository, where the maker of the DT package said something like this may not be possible. However, it is 3 years old, maybe someone has figured out a work around. https://github.com/rstudio/DT/issues/390

I have also tried moving to a different ui type, tuicalendr, which for my purposes works great, but am running into the same issue. My experience in JS is limited, so I have trouble customizing JS within Shiny.

library(shiny)
library(DT)

ui <- fluidPage(
  selectizeInput("input", 
                 label = "",
                 choices = letters[1:26],
                 selected = letters[1]),
DTOutput("datatable")
)


server <- function(input, output) {

output$datatable<- renderDataTable({
  DT::datatable(data.frame(a = as.character(selectizeInput("dtinput",
                                label = "",
                                choices = letters[1:26],
                                selected = letters[1]),
             stringsAsFactors = F)),
             escape = F)
})


}

# Run the application 
shinyApp(ui = ui, server = server)

I am open to other approaches, the ideal would to be to have the data displayed in a calendar view with a checkbox and dropdown menu just below the title. But if I could solve this problem I think I could adapt the code to a calendar myself.

1 Answers

One has to add the javascript functionality manually. The corresponding code would be: $('#ID').selectize().

In order to get the required html code to add, you can run: withTags(selectInput(inputId = "mselect", label = "multi", choices = letters[1:3], multiple = TRUE)). You can extract the required html part from there.

In the docu you will find that you can handover javascript code with JS(): the character options wrapped in JS() will be treated as literal JavaScript code instead of normal character strings.

You want to add the javascript code after the select part is rendered. This is assured by using the initcomplete option.

In order to use values from the input, you have to bind the inputs to shiny:

preDrawCallback = JS('function(){Shiny.unbindAll(this.api().table().node());}'),
drawCallback = JS('function(){Shiny.bindAll(this.api().table().node());}')

As Remko mentioned his linked post is very helpful. Almost all this info is actually available in the post. It might require some experience in shiny to create a reproducible example, so i added one for you:

Reproducible example:

library(shiny)
library(DT)

ui <- fluidPage(

  selectizeInput(
    inputId = "input",
    label = "",
    choices = letters[1:26],
    selected = letters[1]
  ),

  fluidRow(
    DTOutput(outputId = "table")
  )

)

#withTags(selectInput(inputId = "mselect", label = "multi", choices = letters[1:3], 
#    multiple = TRUE))
df <- data.frame(mselect = 
  '<select id="mselect" class="form-control" multiple="multiple">
     <option value="car">car</option>
     <option value="cars">cars</option>
     <option value="dog">dog</option>
  </select>'  
)


js <- c(
  "function(settings){",
  "  $('#mselect').selectize()",
  "}"
)

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

  observe({
    print(input$mselect)
  })

  output$table <- renderDT({

    datatable(
      data = df,
      escape = FALSE,
      options = 
        list(
          initComplete = JS(js),
          preDrawCallback = JS('function(){Shiny.unbindAll(this.api().table().node());}'),
          drawCallback = JS('function(){Shiny.bindAll(this.api().table().node());}')
      )
    )

  })

}

shinyApp(ui = ui, server = server)
Related