reactable
reactable copied to clipboard
Updating selected row based on user input
Hi,
Is there any way to update the selected or expanded row in a reactable
based on another user input?
A simple example can be shown below showing the desired functionality as it can be done in the DT
package with datatables. In this example, the reactable
is shown on the left hand side while the datatable
is shown on the right. In both of these examples, if a row is selected than the respective selectInput
and textOutput
are updated. However, on the datatable
side when the selectInput
is updated the selected row in the datatable
changes. This is done using the dataTableProxy
and selectRows
functions. Is there a way to do something similar to this with the reactable
? This would be an incredibly useful feature if available! Also, in addition to changing which row is selected, if there was a way to change which row is expanded, that would also be very useful!
Example:
library(shiny)
library(reactable)
library(tidyverse)
library(DT)
ui <- shinyUI(
fluidPage(
column(
width = 6,
selectInput(
inputId = "test_select_react",
label = "Select Row",
choices = 1:nrow(mtcars)
),
reactableOutput("test_table_react"),
div(
h5("Selected Rows\n", textOutput("rows_selected_react"))
)
),
column(
width = 6,
selectInput(
inputId = "test_select_dt",
label = "Select Row",
choices = 1:nrow(mtcars)
),
dataTableOutput("test_table_dt"),
div(
h5("Selected Rows\n", textOutput("rows_selected_dt"))
)
)
)
)
server <- shinyServer(
function(input, output, session){
session$onSessionEnded(stopApp)
output$test_table_react <- renderReactable({
reactable(
mtcars,
selection = "single",
selectionId = "selection"
)
})
observe({
# req(input$test_table_selected)
updateSelectInput(
session,
"test_select_react",
selected = input$selection
)
})
output$rows_selected_react <- renderText(input$selection)
output$test_table_dt <- renderDataTable({
datatable(
mtcars,
rownames = FALSE,
selection = "single"
)
})
test_proxy <- dataTableProxy("test_table_dt")
observeEvent(input$test_select_dt, {
selectRows(test_proxy, input$test_select_dt)
})
observe({
updateSelectInput(
session,
"test_select_dt",
selected = input$test_table_dt_rows_selected
)
})
output$rows_selected_dt <- renderText(input$test_table_dt_rows_selected)
}
)
shinyApp(ui, server)
Thanks for the great package!
FYI - I have also posted this question on RStudio Community
Have you had any luck with this @tbradley1013 ? I have a similar issue whereby I need to control the selected rows in a reactable.
Follow these steps with the example below to see what I mean.
- Select all rows in Table 1 (Table 2 is then displayed)
- Select "a" in Table 2
- Unselect "a" in Table 1.
You will see the tick is just passed to "b" in Table 2 as the selectionId tableid2 remains a value of 1. I have tried a few ideas (commented out below) to programatically change the value of tableid2, with no luck.
Ideally I need to deselect all rows in Table 2 when any rows in Table 1 change.
library(reactable)
# reactable * 0.1.0.9000 2019-12-13 [1] Github (glin/reactable@566a4ba)
library(shiny)
library(data.table)
my_data <- data.table(col1 = letters[1:5])
ui <- shinyUI(
fluidPage(
# shinyjs::useShinyjs(),
column(4,
h5('Table 1'),
reactableOutput("test_table_react1")
),
column(4,
h5('Table 2'),
reactableOutput("test_table_react2")
)
)
)
server <- shinyServer(function(input, output, session) {
output$test_table_react1 <- renderReactable({
reactable(
data = my_data,
selection = "multiple",
selectionId = "tableid1",
onClick = "select",
defaultSelected = NULL,
fullWidth = FALSE
)
})
observeEvent(input$tableid1, {
# shinyjs::reset(id = 'tableid2')
# output$test_table_react2 <- renderReactable({NULL})
# HTML('Shiny.setInputValue("tableid2", NULL);')
print(input$tableid2)
})
output$test_table_react2 <- renderReactable({
req(input$tableid1)
reactable(
data = my_data[input$tableid1, ],
selection = "multiple",
selectionId = "tableid2",
onClick = "select",
defaultSelected = NULL,
fullWidth = FALSE
)
})
})
shinyApp(ui, server)
Hi, there isn't a way to do this in the current release version of reactable, but I've been thinking about adding something like a proxy to make this possible. In the development version on GitHub, there's a new experimental updateReactable(outputId, selected = ..., expanded = ...)
function to update the selected or expanded rows. For example:
https://glin.github.io/reactable/articles/examples.html#update-a-reactable-instance
# Requires reactable v0.1.0.9000
# devtools::install_github("glin/reactable")
library(shiny)
library(reactable)
ui <- fluidPage(
actionButton("select_btn", "Select rows"),
actionButton("clear_btn", "Clear selection"),
actionButton("expand_btn", "Expand rows"),
actionButton("collapse_btn", "Collapse rows"),
reactableOutput("tbl"),
"Rows selected:",
verbatimTextOutput("rows_selected")
)
server <- function(input, output) {
output$tbl <- renderReactable({
reactable(
iris,
selection = "multiple",
selectionId = "selection",
onClick = "select",
details = function(index) paste("Row details for row:", index)
)
})
observeEvent(input$select_btn, {
# Select rows
updateReactable("tbl", selected = c(1, 3, 5))
})
observeEvent(input$clear_btn, {
# Clear row selection using NA or integer(0)
updateReactable("tbl", selected = NA)
})
observeEvent(input$expand_btn, {
# Expand all rows
updateReactable("tbl", expanded = TRUE)
})
observeEvent(input$collapse_btn, {
# Collapse all rows
updateReactable("tbl", expanded = FALSE)
})
output$rows_selected <- renderPrint({
print(input$selection)
})
}
shinyApp(ui, server)
If you're able to try it out, let me know what you think. I'm not sure if this will be the final API yet.
Also note that you can only expand or collapse all rows for now. Expanding individual rows may be possible in the future, but it'll be complicated to implement (related: #23).
This is great! Thanks for getting back to me and implementing this!
A couple of follow-up questions based on playing with the dev version this morning:
- When the row selection is updated, is there a way to change which page is "active" based on which row is selected? Currently, if there are 10 rows per page and I select the 11th row via a select Input, that row is selected but the user can't see that with out manually switching to the second page. Obviously this is something that would have to be implemented in the js code, but I am not sure whether it is an option in the react-table library
- Is it possible to add a "selected" html class to the selected row so that I can add some custom CSS to it?
- Is there a running list of the different components in the react-table library that can be made to be updated programatically/dynamically using the this.setState options? I am sure that some would be easier for you to implement than others but the more table attributes that can be updated after the table is already rendered the better (IMO)
It would be useful to be able to expand particular rows, but I can certainly work around that since I can now dynamically select rows!
Thanks again!
-
There wasn't, but I've added a
page
argument so you can change the current page likeupdateReactable("mytbl", selected = 11, page = 2)
(example). -
Love that idea, and I've wanted to do that for a while now as well. I thought it made sense to add a
rowInfo.selected
property to therowClass
/rowStyle
JS functions so you can set CSS classes or styles on selected rows.For example: https://glin.github.io/reactable/articles/examples.html#style-selected-rows
# Requires reactable v0.1.0.9000
# devtools::install_github("glin/reactable")
library(reactable)
reactable(
iris[1:4, ],
selection = "multiple",
defaultSelected = c(1, 3),
rowClass = JS("function(rowInfo) {
return rowInfo.selected ? 'selected' : ''
}"),
rowStyle = JS("function(rowInfo) {
if (rowInfo.selected) {
return { backgroundColor: '#eee', boxShadow: 'inset 2px 0 0 0 #ffa62d' }
}
}"),
borderless = TRUE,
onClick = "select"
)
- I think most of the internal table state can be updated programmatically, and you can find a list of the easier-to-implement ones here: https://github.com/tannerlinsley/react-table/tree/v6#fully-controlled-component. Now that
updateReactable()
exists, these will all probably be added eventually, but feel free to file a feature request if there something you need in particular :)
This is awesome! One last question to go with the updating the selected page. Is there a way to access the number of rows per page? If I restrict it to only being 10 then I can obviously figure out what page needs to be selected based on the row selected. But if I allow the user to select a page length of 10, 50, or 100 would there be any way to access which of those they have selected from the table?
My JS ability is virtually non-existent but I may take a look at the ones you have already implemented and create a PR for any of the others that I might need/want! Thanks for making this an option!
There's a way to get the page size, but it's not easily accessible right now. That would be through the rowClass
/rowStyle
callbacks like:
reactable(
iris,
showPageSizeOptions = TRUE,
rowClass = JS("function(rowInfo, state) {
console.log(state.pageSize)
// Could get it in Shiny using something like:
// Shiny.onInputChange('tbl_page_size', state.pageSize)
}")
)
One idea would be to support similar JS callbacks in updateReactable()
:
updateReactable("tbl", page = JS("function(state) {
// Calculate what page to go to based on state.pageSize
}"))
Another idea would be to add something like a getReactableInfo(outputId)
function, where you can access a reactable instance's state directly in R and Shiny:
getReactableInfo("tbl")
# list(
# pageSize = 20,
# page = 2,
# pages = 8,
# selected = c(3, 4, 5)
# )
This would also be nice to replace the clunky selectionId
for getting selected rows.
Is there one particular way you'd prefer to use? I think I'm leaning toward the second way so far, since it'd be easier to work with in R. But both would be fairly straightforward to implement.
If you ever want to create a PR for anything, I'd be happy to help review or get you started. That also reminds me, I haven't gotten around to writing any development/contributing documentation yet. I'll try to do that soon.
I like the second approach and I think a lot of shiny users will prefer that rather than having to write the JS calback functions. Another benefit would be that as you implement other ways to update the reactable through updateReactable
, I assume, you would be able to add that information to the output of getReactable
which I think would be useful.
Hi @glin
I am not sure whether this is something that falls under this issue or if I should open a new one but it looks like if you pass NA_real_
to updateReactable
for the selected
argument than it successfully deselects the row but the value of the input$selection
value becomes 1 rather than NULL
. See the example below:
library(shiny)
library(reactable)
ui <- fluidPage(
actionButton("select_btn", "Select rows"),
actionButton("clear_btn", "Clear selection"),
actionButton("clear_btn_real", "Clear Selection - NA_real_"),
reactableOutput("tbl"),
"Rows selected:",
verbatimTextOutput("rows_selected")
)
server <- function(input, output) {
output$tbl <- renderReactable({
reactable(
iris,
selection = "multiple",
selectionId = "selection",
onClick = "select",
details = function(index) paste("Row details for row:", index)
)
})
observeEvent(input$select_btn, {
# Select rows
updateReactable("tbl", selected = c(1, 3, 5))
})
observeEvent(input$clear_btn, {
# Clear row selection using NA or integer(0)
updateReactable("tbl", selected = NA)
})
observeEvent(input$clear_btn_real, {
# Expand all rows
updateReactable("tbl", selected = NA_real_)
})
output$rows_selected <- renderPrint({
print(input$selection)
})
}
shinyApp(ui, server)
This issue works for me. Nice catch, I've fixed that in https://github.com/glin/reactable/commit/9f05b906505a5b6d93f93bab0df98c6bc13535fc.
I've also added a getReactableState()
function to the development version: https://glin.github.io/reactable/reference/getReactableState.html
You can use getReactableState(outputId)
to get the current page
, pageSize
, number of pages
, and selected
rows all in a list. Or getReactableState(outputId, "page")
to get just a single value.
Here's an example of it in action: https://glin.github.io/reactable/articles/examples.html#get-the-state-of-a-reactable-instance
Is there a way to edit individual cells in reactable? something similar to DT?
https://blog.rstudio.com/2018/03/29/dt-0-4/
Hi, how to get the current displayed record number in a page, I means the numbers that displayed at left down of a table : (1- 10 of 100 rows displayed)