The teal
framework provides a large catalog of
plug-in-ready analysis modules that can be incorporated into
teal
applications. However, it is also possible to create
your own modules using the module function, which leverages Shiny
modules. Each custom teal module is built as a Shiny module, combining
Shiny’s reactive capabilities with modularized UI and server logic to
encapsulate functionality. This design enables a structured and reusable
approach to creating interactive components that integrate seamlessly
within the teal ecosystem.
In this guide, we will use the simple histogram below as an example,
and demonstrate how to convert this histogram function into a robust
teal
module step-by-step:
my_plot <- hist(
dataset[[vars]],
las = 1,
main = paste("Histogram of", vars),
xlab = vars,
col = "lightblue",
border = "black"
)
This module will allow users to dynamically select datasets and
variables to create histograms within a teal
application.
We will cover best practices, including:
teal_data
object to ensure reactivity and
reproducibility.When developing a custom teal
module for visualizations,
we will first identify the primary inputs that users will interact
with:
dataset
): Allows users
to select which dataset to explore.vars
): Allows users to
choose a specific numeric variable from the chosen dataset, ensuring
only appropriate columns are available for plotting.These inputs are dynamically populated based on the available
datasets and variables in the teal_data
object, which we
will cover later.
teal
Module UIThe UI function defines the controls and display area for the histogram. For this module, we will use:
selectInput
for Dataset: Enables users
to select a dataset from the list of available datasets.selectInput
for Variable: Allows users
to choose a numeric variable from the chosen dataset, dynamically
filtering out any non-numeric columns.plotOutput
for Histogram: Displays the
histogram once both dataset and variable inputs are selected.verbatimTextOutput
for Code:
Automatically displays code that generated the plot based on user
input.Here’s the code for the histogram_module_ui
function:
# UI function for the custom histogram module
histogram_module_ui <- function(id) {
ns <- shiny::NS(id)
shiny::tagList(
shiny::selectInput(ns("dataset"), "Select Dataset", choices = NULL),
shiny::selectInput(ns("variable"), "Select Variable", choices = NULL),
shiny::plotOutput(ns("histogram_plot")),
shiny::verbatimTextOutput(ns("plot_code")) # To display the reactive plot code
)
}
teal
Module ServerThe server function is where the main logic of a teal
module is handled. For our histogram module, the server function will
handle user interactions and manage the reactive teal_data
object, which allows the module to dynamically respond to user
inputs.
data
Argument to the Server FunctionTo begin, it’s essential to include the data
argument in
the server function definition.
This data
argument holds the reactive
teal_data
object, which contains your datasets and any
filters applied. By including data
, we can ensure:
teal_data
, allowing it to automatically respond to
changes.The correct function definition for the server function is:
histogram_module_server <- function(id, data) {
moduleServer(id, function(input, output, session) {
# Server logic goes here
})
}
If you need a refresher on the teal_data
object, please
visit the teal.data
package documentation.
teal_data
as a Reactive Object in Server
LogicWhen used in the server logic of a teal
module, the
teal_data
object becomes a reactive data
container. This means that to access its contents, you need to
call it like a function, using parentheses: data()
.
This syntax triggers reactivity, ensuring that the data within
teal_data
stays up-to-date with any filters or changes
applied elsewhere in the application.
Note: The
teal_data
object behaves as a reactive data container only when used within the server logic. If accessed outside of the server, it will not be reactive.
names()
to Access Dataset Names in
teal_data
objectThe teal_data
object can contain multiple datasets. To
retrieve the names of these datasets, use the names()
function:
This will return a character vector of the dataset names contained in
teal_data
. You can then use these names to dynamically
populate input controls, like a dataset selection drop-down.
[[ ]])
To access an individual dataset from teal_data
, use
double brackets ([[ ]]
) along with the dataset name. This
allows you to extract the specific dataset as a data frame:
Here, input$dataset
represents the name of the dataset
selected by the user. This syntax is highly flexible because it
dynamically references whichever dataset the user has chosen. You can
further subset or manipulate this extracted data frame as needed.
teal_data
and Dynamic
Variable InjectionIn this updated server function, we will perform the following:
new_data
as a modified version
of data()
using within()
, dynamically
injecting input$dataset
and
input$variable
.renderPlot()
displays
the plot by referencing the plot stored in the updated
teal_data
object, new_data
.Here’s the code:
# Server function for the custom histogram module with injected variables in within()
histogram_module_server <- function(id, data) {
moduleServer(id, function(input, output, session) {
# Update dataset choices based on available datasets in teal_data
shiny::observe({
shiny::updateSelectInput(
session,
"dataset",
choices = names(data())
)
})
# Update variable choices based on selected dataset, only including numeric variables
observeEvent(input$dataset, {
req(input$dataset) # Ensure dataset is selected
numeric_vars <- names(data()[[input$dataset]])[sapply(data()[[input$dataset]], is.numeric)]
shiny::updateSelectInput(session, "variable", choices = numeric_vars)
})
# Create a reactive `teal_data` object with the histogram plot
result <- reactive({
req(input$dataset, input$variable) # Ensure both dataset and variable are selected
# Create a new teal_data object with the histogram plot
new_data <- within(
data(),
{
my_plot <- hist(
input_dataset[[input_vars]],
las = 1,
main = paste("Histogram of", input_vars),
xlab = input_vars,
col = "lightblue",
border = "black"
)
},
input_dataset = as.name(input$dataset), # Replace `input_dataset` with input$dataset
input_vars = input$variable # Replace `input_vars` with input$variable
)
new_data
})
# Render the histogram from the updated teal_data object
output$histogram_plot <- shiny::renderPlot({
result()[["my_plot"]] # Access and render the plot stored in `new_data`
})
# Reactive expression to get the generated code for the plot
output$plot_code <- shiny::renderText({
teal.code::get_code(result()) # Retrieve and display the code for the updated `teal_data` object
})
})
}
Let’s review what we’ve done so far:
within()
:
input_dataset = as.name(input$dataset)
passes the
dataset name dynamically as input_dataset
.input_vars = input$variable
passes the selected
variable name directly as input_vars
.within()
, my_plot
uses these
injected variables to dynamically generate the histogram plot.output$histogram_plot
uses renderPlot()
to
display the plot stored in new_data
by referencing
result()[["my_plot"]]
.output$plot_code
render function displays the
dynamically generated code using
teal.code::get_code(result())
, allowing users to see the
exact code used to generate the plot reactively.teal
Module FunctionThe teal::module()
function allows you to encapsulate
your UI and server logic into a teal
module, making it
reusable and ready to integrate into any teal
application.
By setting datanames = "all"
, you give the module access
to all datasets specified in the teal_data
object.
teal
Module into a
teal
AppWith the custom teal
module set up, it can now be
integrated into a teal
app. We’ll use init()
from teal
to specify the datasets and modules used in the
app, then run the app to test the newly created module.
library(teal)
# Define datasets in `teal_data`
data_obj <- teal_data(
iris = iris,
mtcars = mtcars
)
# Initialize the teal app
app <- init(
data = data_obj,
modules = modules(create_histogram_module())
)
# Run the app
shiny::shinyApp(ui = app$ui, server = app$server)
Congratulations! You just created a custom teal module and used it in a teal app!
This setup provides a fully dynamic, user-controlled
teal
module that allows for interactive data exploration
and code visibility, enhancing both usability and transparency.
Now that you’ve mastered the essentials of building and integrating
modules in teal
, you’re ready to explore more advanced
features. teal
offers a wide range of capabilities to
enhance your module’s functionality and user experience.
Enhance your custom teal
module with reporting features!
Dive into this vignette
to see just how simple it is to add powerful reporting capabilities and
elevate your module’s impact.
The teal.widgets
package provides various widgets which can be leveraged to quickly
create standard elements in your custom teal
module.