I am making a shiny app where the user selects objects and data in selected objects sums to totals in a nested reactable
table. I am using a reactible
table, with DT
tables nested inside (maybe not ideal, but in the real app I need some functionalities of the nested tables which I only know how to do with DT
). Each time the user selects things, the underlying data creating the tables changes, but I want to be able to click and see those changes without re-rendering the entire table and collapsing the nests that the user had expanded. Is there a way to do this in Shiny?
I thought updateReactable
might be the way, but I couldn't figure out how to make it work.
I made a dumb little example here, where the user selects locations in the kitchen that they will look for ingredients for a meal. The table shows how many ingredients of each type the user finds enough of for the recipe, then the expand button shows a table of individual ingredients. I want to be able to click the UI checkbox and not have the table completely reset each time.
Thanks,
library(shiny)
library(reactable)
library(DT)
library(tibble)
library(dplyr)
ingredients <- tribble(
~location, ~ingredient, ~type, ~quantity,
"Cabinet 1" , "carrots", "produce", 2,
"Cabinet 1" , "potatoes", "produce", 4,
"Cabinet 1" , "pasta", "dry goods", 2,
"Cabinet 2" , "bouillon", "dry goods", 1,
"Cabinet 2" , "spices", "dry goods", 2,
"Cabinet 2" , "pasta", "dry goods", 1,
"Refridgerator" , "chicken", "meat", 1,
"Refridgerator" , "celery", "produce", 2,
"Refridgerator" , "onion", "produce", 2,
"Refridgerator" , "carrots", "produce", 3,
)
recipe_needs <-
tribble(
~ingredient, ~type, ~quantity_needed,
"carrots", "produce", 5,
"potatoes", "produce", 4,
"pasta", "dry goods", 4,
"bouillon", "dry goods", 1,
"spices", "dry goods", 5,
"chicken", "meat", 1,
"celery", "produce", 2,
"onion", "produce", 2,
)
ui <- fluidPage(
checkboxGroupInput("checkGroup", label = h3("Check location"),
choices = unique(ingredients$location)),
br(),br(),br(),
reactableOutput("nested_table"),
)
server <- function(input, output) {
# make dataframes
ingredients_totals <- reactiveValues(
# all ingredients you found in your kitchen
ingredients_found = ingredients %>%
distinct(ingredient,type) %>%
arrange(type) %>%
mutate(quantity_found = 0),
# how many ingredients where you have all you need, by type
sums_by_type = recipe_needs %>%
mutate(value = 0) %>%
mutate(success = case_when(value >= quantity_needed ~ 1,
TRUE ~ 0)) %>%
group_by(type) %>%
summarize(success = paste0(sum(success), "/", n()))
)
observe( {
# sum up the ingredients in the places you looked
location_totals <-
ingredients %>%
mutate(quantity_found = case_when(location %in% input$checkGroup ~ quantity,
TRUE ~ 0)) %>%
group_by(ingredient) %>%
summarize(quantity_found = sum(quantity_found)) %>%
arrange(ingredient)
# add ingredients found in your locations to a totals df
ingredients_totals$ingredients_found <-
arrange(ingredients_totals$ingredients_found,ingredient) %>%
mutate(quantity_found = location_totals$quantity_found) %>%
left_join(recipe_needs)
# calculate how many ingredients you have enough of
ingredients_totals$sums_by_type = recipe_needs %>%
arrange(ingredient) %>%
mutate(value = location_totals$quantity_found) %>%
mutate(success = case_when(value >= quantity_needed ~ 1,
TRUE ~ 0)) %>%
group_by(type) %>%
summarize(success = paste0(sum(success), "/", n()))
})
# make nested table
output$nested_table <-
renderReactable(
reactable(ingredients_totals$sums_by_type,
details = function(index) {
typesums <- filter(ingredients_totals$ingredients_found,
type == sort(unique(ingredients_totals$ingredients_found$type))[index]) %>% select(-type)
tbl <- datatable(typesums,options = list(dom = 't'))
},
))
}
shinyApp(ui, server)