Simplifying Parts Of A Shiny App by Creating Functions
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
Until now, the Shiny app we created has all the HTML in the UI. On top of that, we have repeated code that we can stick into a function to make the app smaller and to be able to test the functions we are creating.
Here are Part 1 and Part 2 of the app.
The UI Shiny Part
Right now, the UI part of the shiny app looks like this:
ui <- fluidPage( br(), br(), div(id = "placeholder"), shiny::tagList( # first viz div( class = "class_a", div( class = "col-md-6", div( class = "panel panel-default", div( class = "panel-heading clearfix", tags$h2("Visualization 1", class = "pull-left panel-title"), div( class = "pull-right", shiny::actionButton( inputId = "a", label = "", class = "btn-danger delete", icon = shiny::icon("minus") ) ) ), div( class = "panel-body", plotly::plot_ly(mtcars, x = ~mpg, y = ~wt) ) ) ) ), # second viz div( class = "class_b", div( class = "col-md-6", div( class = "panel panel-default", div( class = "panel-heading clearfix", tags$h2("Visualization 2", class = "pull-left panel-title"), div( class = "pull-right", shiny::actionButton( inputId = "b", label = "", class = "btn-danger delete", icon = shiny::icon("minus") ) ) ), div( class = "panel-body", plotly::plot_ly(mtcars, x = ~mpg, y = ~wt) ) ) ) ), # third viz div( class = "class_c", div( class = "col-md-6", div( class = "panel panel-default", div( class = "panel-heading clearfix", tags$h2("Visualization 3", class = "pull-left panel-title"), div( class = "pull-right", shiny::actionButton( inputId = "c", label = "", class = "btn-danger delete", icon = shiny::icon("minus") ) ) ), div( class = "panel-body", plotly::plot_ly(mtcars, x = ~mpg, y = ~wt) ) ) ) ) ), shiny::includeScript(here::here("GA_dashboard/part_2/www/scripts.js")) )
The code produces three visualizations. Instead of copying and pasting the code three times, we are going to write a function that produces the three visualizations.
All we need to do is to identify the lines that are changing and turn them into arguments.
The things that are unique to each visualization are:
- The class inside the first
div
- The
h2
header in the panel heading - The input id of the action button
- The class of the action button and color are the same but in case we want to change them we will turn them into arguments as well
Creating a Function for the Shiny UI
Putting everything together, the function looks like this:
google_analytics_viz <- function(title = NULL, btn_id, class_all, class_specific, color) { shiny::tagList( div( class = class_specific, div( class = "col-md-6", div( class = "panel panel-default", div( class = "panel-heading clearfix", tags$h2(title, class = "panel-title pull-left"), div( class = "pull-right", shiny::actionButton( inputId = btn_id, label = "", class = stringr::str_glue("btn-{color} {class_all}"), icon = shiny::icon("minus") ) ) ), div( class = "panel-body", plotly::plot_ly(mtcars, x = ~mpg, y = ~wt) ) ) ) ) ) }
What we can do next is to replace the code inside the fluidPage
with an uiOutput
function and then put a renderUI
inside the server.
The shiny UI looks like this now:
ui <- fluidPage( br(), br(), div(id = "placeholder"), shiny::tagList( shiny::uiOutput( outputId = "first" ) ), shiny::includeScript(here::here("GA_dashboard/part_2/www/scripts.js")) )
And the server side looks like this:
server <- function(input, output) { output$first <- shiny::renderUI({ pmap( list(x = c("a", "b", "c"), y = c("Viz 1", "Viz 2", "Viz 3")), function(x, y) { google_analytics_viz( title = y, viz = y, btn_id = x, df = NULL, class_all = "delete", class_specific = paste0("class_", x), color = "danger" ) } ) }) # run when we add visualization shiny::observeEvent(input$add_btn_clicked, { # clicked id panel <- input$add_btn_clicked panel_plot_item <- google_analytics_viz( title = input$header, viz = NULL, df = NULL, btn_id = panel, class_all = "delete", class_specific = paste0("class_", panel), color = "danger" ) css_selector <- ifelse(input$last_panel == "#placeholder", "#placeholder", paste0(".", input$last_panel) ) shiny::insertUI( selector = css_selector, "afterEnd", ui = panel_plot_item ) }) }
Conclusions
We significantly reduced the lines of code in the shiny application. As a result of that, the code is more readable and clearer, and functions can also be unit tested.
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.