I am developing an application in R Shiny that displays a table using the reactable package. I am trying to add a custom search field that searches all the columns in the table. Looking at Greg Lin's example codes there is one that fits perfectly ‘custom search input’ but I can't adapt it for Shiny. This is my code:
library(shiny)
library(reactable)
library(htmltools)
ui <- fluidPage(
# Interface elements
titlePanel("Custom Search in reactable"),
fluidRow(
div(
style = "margin-bottom: 0.75rem",
tags$input(
type = "text",
placeholder = "Search for cars...",
style = "padding: 0.25rem 0.5rem; width: 100%",
oninput = "Reactable.setSearch('cars-search-table', this.value)"
)
),
# Show reactable
div(id = "table-content", reactableOutput("contents"))
)
)
server <- function(input, output) {
# Render reactable
output$contents <- renderReactable({
# Load data
data <- MASS::Cars93[1:15, c("Manufacturer", "Model", "Type", "Price")]
reactable(data,
defaultPageSize = 5,
elementId = "cars-search-table")
})
}
shinyApp(ui, server)
My example works perfectly. It uses the elementId to indirectly fill the search field that has the reactable but when I run the application I get this warning:
Warning in renderWidget(instance) : Ignoring explicitly provided widget ID ‘cars-search-table’; Shiny doesn't use them
I don't know if this is the best alternative but I had thought about using shinyjs to change the elementId after the reactable is rendered but I couldn't get it to work properly:
library(shiny)
library(reactable)
library(htmltools)
library(shinyjs)
ui <- fluidPage(
useShinyjs(), # Incluir shinyjs en la UI
# Interface elements
titlePanel("Custom Search in reactable"),
fluidRow(
div(
style = "margin-bottom: 0.75rem",
tags$input(
id = "search-input",
type = "text",
placeholder = "Search for cars...",
style = "padding: 0.25rem 0.5rem; width: 100%"
)
),
# Show reactable
div(id = "table-content", reactableOutput("contents"))
)
)
server <- function(input, output, session) {
# Render reactable
output$contents <- renderReactable({
# Load data
data <- MASS::Cars93[1:15, c("Manufacturer", "Model", "Type", "Price")]
reactable(data,
defaultPageSize = 5,
elementId = NULL) # No usamos elementId aquí
})
# Modify the table after it has been rendered
observe({
runjs("document.querySelector('#table-content > div').id = 'cars-search-table';")
# Connect the search input to the table
runjs("
document.getElementById('search-input').oninput = function() {
Reactable.setSearch('cars-search-table', this.value);
};
")
})
}
shinyApp(ui, server)
UPDATE: as suggested by user @masgusl I tried using crosstalk. This is the code I generated:
library(shiny)
library(reactable)
library(htmltools)
library(crosstalk)
ui <- fluidPage(
# Interface elements
titlePanel("Custom Search in reactable"),
fluidRow(
div(
style = "margin-bottom: 0.75rem",
tags$input(
type = "text",
id = "search",
placeholder = "Search for cars...",
style = "padding: 0.25rem 0.5rem; width: 100%"
)
),
# Show reactable
div(id = "table-content", reactableOutput("contents"))
)
)
server <- function(input, output) {
# Load data
data <- MASS::Cars93[1:15, c("Manufacturer", "Model", "Type", "Price")]
# Create crosstalk shared data object
shared_data <- SharedData$new(data)
# Create crosstalk filter
filtered_data <- reactive({
if (input$search == "") {
shared_data$data()
} else {
shared_data$data() %>%
filter(if_any(everything(), ~ grepl(input$search, ., ignore.case = TRUE)))
}
})
# Render reactable
output$contents <- renderReactable({
reactable(filtered_data(),
defaultPageSize = 5
)
})
}
shinyApp(ui, server)
In this case, the code works perfectly but for a very large dataset, when you filter the data it is quite noticeable the waiting time until the filtered data is displayed. I don't know if the code is optimised.
Could anyone help me to find a solution as simple as possible, please?. Perhaps with a more appropriate solution.
Thank you very much.
Finally, thanks to @Jan's comment, I checked out another post, which in turn led me to the documentation on reactable and how to use the javascript API. This section describes that for Shiny applications, the elementId is taken directly from the Shiny output ID specified in reactableOutput(), so the problem in my example code is solved by removing the elementId and changing the reference in 'Reactable.setSearch'. My code looks like this:
library(shiny)
library(reactable)
library(htmltools)
ui <- fluidPage(
# Interface elements
titlePanel("Custom Search in reactable"),
fluidRow(
div(
style = "margin-bottom: 0.75rem",
tags$input(
type = "text",
placeholder = "Search for cars...",
style = "padding: 0.25rem 0.5rem; width: 100%",
oninput = "Reactable.setSearch('contents', this.value)"
)
),
# Show reactable
div(id = "table-content", reactableOutput("contents"))
)
)
server <- function(input, output) {
# Render reactable
output$contents <- renderReactable({
# Load data
data <- MASS::Cars93[1:15, c("Manufacturer", "Model", "Type", "Price")]
reactable(data,
defaultPageSize = 5)
})
}
shinyApp(ui, server)
Knowing this, the solution was very simple and without the need to use crosstalk. At least in this case.
Thanks to all of you for your comments. In the end, you all helped me to look for other alternatives that I will be able to use sometime.
Thanks again
Wardiam