Hi everyone! Thank you in advance for taking the time to help me with this (it's greatly appreciated )
I have an application I built using modules with shinydashboard. The input data is a list (two versions of the palmerpenguins
data), and I want the selectInput()
to update when the user clicks on the second menuItem()
.
EDIT: The packages and data:
# packages ----------------------------------------------------------------
library(shiny)
library(tidyverse)
library(shinydashboard)
library(reactable)
library(palmerpenguins)
library(janitor)
# raw_penguins -----
# clean this up a little for printing
raw_penguins <- penguins_raw %>%
janitor::clean_names() %>%
# rename this to 'ratio of stable isotopes' (_rsi)
rename(
delta_15_rsi = delta_15_n_o_oo,
delta_13_rsi = delta_13_c_o_oo
) %>%
mutate(
sex = str_to_lower(sex),
sex = factor(sex),
species = factor(species),
island = factor(island)
)
# split data by species and island ----
penguins_raw_list <- split(x = raw_penguins, ~island)
penguins_list <- split(x = penguins, ~species)
# all_penguins_list -----
all_penguins_list <- list(
"penguins_raw" = penguins_raw_list,
"penguins" = penguins_list
)
Here is the UI module:
# penguin_module_UI -----
penguin_module_UI <- function(id) {
tagList(
shiny::selectInput(
inputId = NS(namespace = id, id = "dataset"),
label = "Dataset", choices = c("Adelie", "Chinstrap", "Gentoo"),
selected = "Adelie"
),
br(), br(),
reactable::reactableOutput(NS(id, "penguin_table")),
br(), br(),
shiny::verbatimTextOutput(outputId = NS(namespace = id, id = "values"))
)
}
And there is the server module:
# penguin_module_server ---------
penguin_module_server <- function(id, dataset) {
moduleServer(id, function(input, output, session) {
# sidebarMenu id ----
observeEvent(eventExpr = input$sbmenu, handlerExpr = {
updateSelectInput(inputId = input$dataset,
choices = c("Torgersen", "Dream", "Biscoe"),
selected = "Biscoe"
)
})
table_data <- reactive({
if (dataset == "penguins") {
list_of_penguins <- all_penguins_list$penguins
table_data <- as_tibble(
list_of_penguins[[input$dataset]]
)
} else {
list_of_penguins_raw <- all_penguins_list$penguins_raw
table_data <- as_tibble(
list_of_penguins_raw[[input$dataset]]
)
}
return(table_data)
})
# penguin_table -----
output$penguin_table <- reactable::renderReactable({
req(input$dataset)
reactable::reactable(
data = table_data(),
# reactable settings ------
defaultPageSize = 10,
resizable = TRUE,
highlight = TRUE,
height = 350,
wrap = FALSE,
bordered = TRUE,
searchable = TRUE,
filterable = TRUE
)
})
# reactive values ----
output$values <- shiny::renderPrint({
req(input$dataset)
all_values <- reactiveValuesToList(x = input,
all.names = TRUE)
values <- str_detect(names(all_values), "reactable", negate = TRUE)
print(all_values[values])
})
})
}
I'm using the observeEvent()
here, and the dataset
argument is passed to the penguin_module_server()
in the demo below:
# penguin_module_demo ----------
penguin_module_demo <- function() {
# UI -----------------
ui <- dashboardPage(
dashboardHeader(title = "Penguin Modules"),
dashboardSidebar(
# sidebarMenu id ----
sidebarMenu(id = "sbmenu",
menuItem("Penguin (species)",
tabName = "penguins",
icon = icon("table")
),
menuItem("Penguins Raw (island)",
tabName = "penguins_raw",
icon = icon("table")
)
)
),
dashboardBody(
tabItems(
tabItem(
tabName = "penguins",
fluidRow(
box(
title = "Penguins (species)",
width = 12,
penguin_module_UI("penguins_table")
)
)
),
tabItem(
tabName = "penguins_raw",
fluidRow(
box(
title = "Penguins Raw (island)",
width = 12,
penguin_module_UI("penguins_raw_table")
)
)
)
)
)
)
# server ---------
server <- function(input, output) {
# Penguins -----
penguin_module_server("penguins_table", dataset = "penguins")
# Penguins Raw ------
penguin_module_server("penguins_raw_table", dataset = "penguins_raw")
}
# run -----
shinyApp(ui = ui, server = server)
}
penguin_module_demo()
I think I know what the issue is (from the reactiveValuesToList()
output I'm rendering):
- the module's namespace only contains the
inputId
for the dataset, which I define inpenguin_module_UI()
- when the
observeEvent()
goes looking for theinput$sbmenu
, it doesn't see it because it's created outside the module.
If I'm right, is there a way to include the sidebarMenu(id)
inside the UI (maybe inside the tagList()
)?
Thank you again so much in advance for helping me tackle this one (still learning modules )
EDIT: I deployed the app with the error here