library("tidyverse")
library("rfigshare")
library("lubridate")
fs_deposit_id <- 3761562
deposit_details <- fs_details(fs_deposit_id)
deposit_details <- unlist(deposit_details$files)
deposit_details <- data.frame(
split(deposit_details, names(deposit_details)),
stringsAsFactors = FALSE
)
imported_country_group_data <- deposit_details |>
filter(str_detect(name, "bcountrydata_")) |>
pull(download_url) |>
read_csv() |>
mutate(timestamp = as_date(timestamp)) |>
rename(date = timestamp)
gigs_by_country_group <- imported_country_group_data |>
group_by(date, country_group) |>
summarise(jobs = sum(count)) |>
ungroup()
gigs_by_occupation <- imported_country_group_data |>
group_by(date, occupation) |>
summarise(jobs = sum(count)) |>
ungroup()Shiny Modules are an “advanced” feature of Shiny apps that developers can use to reduce code duplication, simplify complex inter-relating controls, and allow UI elements to be compartmentalised into R packages.
Much of the stuff written about modules gets lost in the weeds. We’re going to make the Shiny app embedded below, both with and without modules.
The app displays data from the Online Labour Index — a project from the University of Oxford’s Internet Institute studying the online labour market. The data is deposited on Figshare: DOI: 10.6084/m9.figshare.3761562.
Data import and wrangling
Create a script file called data-processing.R:
gg_ma_timeseries functions
Create a file called gg_ma_timeseries.R:
library("ggsci")
library("xts")
gg_ma_timeseries <- function(.data, date, value, category) {
date <- enquo(date)
value <- enquo(value)
category <- enquo(category)
n_colours <- .data |> pull(!!category) |> unique() |> length()
colours_from_startrek <- colorRampPalette(pal_startrek(
palette = c("uniform")
)(7))(n_colours)
.data |>
ggplot(aes(x = !!date, y = !!value, color = !!category)) +
geom_line() +
theme_bw() +
scale_color_manual(values = colours_from_startrek) +
scale_x_date(expand = c(0.01, 0.01)) +
scale_y_continuous(expand = c(0.01, 0)) +
labs(
title = "Online Labour Index data",
subtitle = "DOI:10.6084/m9.figshare.376156"
)
}
ma_job_count <- function(.data, date, value, category, window_width) {
date <- enquo(date)
value <- enquo(value)
category <- enquo(category)
window_width <- as.numeric(window_width)
.data |>
group_by(!!category) |>
arrange(!!date) |>
mutate(
!!value := rollmean(
!!value,
k = window_width,
na.pad = TRUE,
align = "right"
)
) |>
filter(!is.na(!!value)) |>
ungroup()
}server.R skeleton
library("shiny")
library("tidyverse")
library("rfigshare")
library("lubridate")
library("xts")
library("ggsci")
source("data-processing.R", local = TRUE)
source("gg_ma_timeseries.R", local = TRUE)
function(input, output, session) {}Shiny app without modules
The module-free version is available at: github.com/charliejhadley/training_shiny-module
usethis::use_course(
"https://github.com/charliejhadley/training_shiny-module/raw/master/gg_ma_timeseries/shiny-without-modules.zip"
)The ui.R file duplicates the radioButtons() controls for each tab, and the server.R file duplicates the renderPlot() and renderUI() calls. Modules solve this.
Introducing the Shiny Module
The module has two components:
gg_ma_timeseries_input()creates an instance of the controlsgg_ma_timeseries_output()creates an instance of the chart
Create /modules/client-side_gg-ma-timeseries.R:
library("shinycustomloader")
gg_ma_timeseries_input <- function(id) {
ns <- NS(id)
tagList(
"This entire tab is a shiny module, including this text, the radio buttons and the chart.",
fluidRow(column(
radioButtons(
ns("landing_rollmean_k"),
label = "",
choices = list(
"Show daily value" = 1,
"Show 28-day moving average" = 28
),
selected = 28,
inline = TRUE
),
width = 12
))
)
}
gg_ma_timeseries_output <- function(id) {
ns <- NS(id)
withLoader(plotOutput(ns("ma_plot")), type = "html", loader = "dnaspin")
}Key rules for modules:
- Place module code in a
/modulessubfolder. - Source input code in
ui.R, output code inserver.R. ns <- NS(id)ensures you’re referring to the correctinput/outputnamespace.- If returning multiple UI elements, wrap them in
tagList().
Update ui.R to use the module:
library("shiny")
source("modules/client-side_gg-ma-timeseries.R", local = TRUE)
shinyServer(navbarPage(
"Shiny Modules",
tabPanel(
"By occupation",
fluidPage(
gg_ma_timeseries_input("occupation_controls"),
gg_ma_timeseries_output("occupation_chart")
)
),
tabPanel(
"By country",
fluidPage(
gg_ma_timeseries_input("by_country_controls"),
gg_ma_timeseries_output("by_country_chart")
)
)
))