$return_data[[dataset_name]] <<- mod_data(id = dataset_name, datasetname = dataset_name) rv
This is a simple shiny app that demonstrates dynamic shiny tabs. Each tab contains a shiny module which returns a series of data parameters and tabs can be added and removed. In one use-case this construct was used in three higher level tabs, returning a complex, nested structure.
Code is available at: https://github.com/harveyl888/shiny_dynamic_tabs
The concept is fairly simple - upon start up a tabsetPanel is built with a button prepended before the first tab. When clicked, this button adds a new tab containing a shiny module (the tab also contains a close button). The shiny module returns a series of data and these data, from all tabs, are stored in a reaciveValues list (rv$return_data
). As tabs are added, deleted or their content changed, the contents of rv$return_data
updates.
tabsetPanel code
The code below builds the tabsetPanel. Upon execution, rv$dataset_count
is incremented from 0 to 1, dataset_name
is set to dataset_1 and added to the rv$dataset_names
list, and rv$trigger_add_data_button
is changed from FALSE
to TRUE
. In addition, the first tab is created, containing a shiny module which returns data to rv$return_data
. The line:
ensures that the shiny module returns data to a named list using the tab name itself as the component name.
## tabs
$ui_tabs <- renderUI({
outputisolate({
$dataset_count <- rv$dataset_count + 1
rv<- paste0("dataset_", rv$dataset_count)
dataset_name $dataset_names[[length(rv$dataset_names) + 1]] <- dataset_name
rv$return_data[[dataset_name]] <<- mod_data(id = dataset_name, datasetname = dataset_name)
rv$trigger_add_data_button <- TRUE
rv
})tabsetPanel(id = "tab_data",
tabPanel(title = tab_title(dataset_name), value = dataset_name, mod_data_UI(dataset_name)))
})
Add a new tab with data
The add_dataset
function is run whenever the button in the tabsetPanel is clicked. This function adds a new tab containing a shiny module. It is very similar to the tabsetPanel code
## function to add a new dataset
<- function() {
add_dataset $dataset_count <- rv$dataset_count + 1
rv<- paste0("dataset_", rv$dataset_count)
dataset_name $dataset_names[[length(rv$dataset_names) + 1]] <- dataset_name
rv$return_data[[dataset_name]] <<- mod_data(id = dataset_name, datasetname = dataset_name)
rvappendTab(inputId = "tab_data", tabPanel(title = tab_title(dataset_name), value = dataset_name, mod_data_UI(dataset_name)))
}
Creating a new tab
Finally, the function below, builds a title for a tab including a close button.
## tab title with close button
<- function(name, type = "data") {
tab_title $span(
tags
name,$span(icon("times"),
tagsstyle = "margin-left: 5px;",
onclick = paste0("Shiny.setInputValue(\"", paste0("remove_", type, "_tab"), "\", \"", name, "\", {priority: \"event\"})"))
) }
When the close icon is clicked the following observeEvent is triggered, removing the tab and updating rv$dataset_names
, a list of the names of the current tabs.
## remove a dataset
observeEvent(input$remove_data_tab, {
removeTab(inputId = "tab_data", target = input$remove_data_tab)
isolate({rv$dataset_names <- rv$dataset_names[!rv$dataset_names == input$remove_data_tab]})
})
Screen Captures
Upon start up, the tabPanel is built with a button and a single tab. Data returned is printed to the right of the tabPanel.
Adding additional tabs and changing their content updates the returned data.
Removing tab #2 updates the returned data.