::use_course("https://github.com/charliejhadley/training_shiny-module/raw/master/wdi_indicators_modules/01_without-modules.zip") usethis
Shiny apps are awesome, with a bit of training you can build fairly impressive interactive web apps. But at some point, the subject of “shiny modules” will rear its head.
When I started learning modules, I found most of the existing articles focus on the technical-side of things and don’t focus on the benefits of using modules - they can significantly simplify and improve your apps.
I’m going to put together a few different tutorials on real-world Shiny apps and how modules can be used to improve them. As these tutorials develop I’ll link to them below.
But for now, we’re going to build this Shiny app:
The shiny app displays data from the
WDI
packageEach “page” of the Shiny app details a different “development indicator” from the
WDI
packageUsers select a country of interest from a pull-down menu
The chart, text and table underneath the pull-down menu all update when a country is selected
The charts, text and tables are the same on each page except for two variables; the selected country and the indicator detailed on that page.
How to use this tutorial
It’s often useful to skim read through a tutorial before attempting to run the code on your own machine. If you do want to follow along with the code, you will need to install the {usethis}
package before starting.
This tutorial is split into
Shiny app without modules
Shiny app
Shiny app without modules
The module-free version of the Shiny app can be downloaded onto your machine by running this code:
Once the RStudio project has opened, let’s take a look at the structure of the ui.R
file in the app:
<- c(...)
countries_list
navbarPage(
"WDI",
tabPanel(
"Internet",
fluidPage(
selectInput("internet_country",
choices = countries_list),
...
)
),tabPanel(
"Bank branches",
fluidPage(
selectInput("bank_branches_country",
choices = countries_list),
...
)
),
... )
We’re essentailly duplicating the same selectInput()
in each of our tabPanel()
s. If there were many controls being repeated we could make an argument for using modules from this file alone.
Let’s take a look at the server.R
file of this app:
function(input, output, session){
$internet_timeline <- renderPlot({
output
%>%
wdi_data gg_wdi_indicator_timeline(input$internet_country,
...)
})
$internet_comparison_table <- renderUI({
output
<- wdi_data %>%
ranking_table filter(country == input$internet_country) %>%
%>%
ranking_table datatable()
})
$bank_branches_timeline <- renderPlot({
output
%>%
wdi_data gg_wdi_indicator_timeline(input$bank_branches_country,
...)
})
$bank_branches_comparison_table <- renderUI({
output
<- wdi_data %>%
ranking_table filter(country == input$bank_branches_country) %>%
...
%>%
ranking_table datatable(...)
})
}
There’s a lot of duplication in this file. If we wanted to add a new tab about the number of secondary school students, we would have to add all of the following:
## ui.R
tabPanel(
"Secondary schools",
fluidPage(
selectInput("secondary_schools_country",
choices = countries_list),
...
)
)## server.R
$secondary_schools_timeline <- renderPlot({
output
%>%
wdi_data gg_wdi_indicator_timeline(input$secondary_schools_country,
...)
})
$secondary_schools_comparison_table <- renderUI({
output
<- wdi_data %>%
ranking_table filter(country == input$secondary_schools_country) %>%
...
%>%
ranking_table datatable(...)
})
Let’s breakdown the advantages to re-writing this app to use modules.
What would be the benefits of switching to use modules?
Without modules, if we wanted to change the look of the “comparison tables” we would need to do that X times - once for each
output$*_comparison_table
object.
Modules therefore help reduce transcription or copy/paste errors.Modules will allow us to change the
Modules will reduce script file length, making the code easier to read and understand
Currently, if we wanted to change
If we needed to add another tab to our
Each time we add a new tab to our shiny app, we’ll need to create a new pair of render*()
functions and corresponding inputs in the ui.R
file.
By re-designing our app to use modules, we’ll benefit from the following:
- Reduced script file lenght, improving readability
- Simpler feature updates, changing the module code will update all pages at once.
Reuse
Citation
@online{hadley2019,
author = {Charlie Hadley},
title = {Shiny Modules to Reduce Duplication in Apps},
date = {2019-07-09},
url = {https://visibledata.co.uk/posts/2019-07-09-shiny-modules-to-reduce-duplication-in-apps},
langid = {en}
}