Building a Google Analytics Dashboard With R Shiny From Scratch – Part 2
August 31, 2022 By Pascal Schmidt Personal Project R R Shiny
Last week we started out building the Google Analytics Dashboard for my blog. This week we are continuing with adding some functionality to the UI with a bit of JavaScript.
We added panels with visualizations to the page that can be deleted when we are clicking the action button. Once the visualization is deleted, we get back an action link. This time, I want to implement the functionality of adding the visualization back into the page when we click the action link.
Adding Back Visualizations
Again, we need some JS + jQuery
// add plot and remove action link $(document).on("click", ".added_btn", function() { clicked_id = $(this).attr('id'); p = $("#" + clicked_id).parent().text(); p = $.trim(p); Shiny.setInputValue('header', p, {priority: 'event'}); $(".added_" + clicked_id).remove(); if($("[class^='class_']").length) { last_panel = $("[class^='class_']").last().attr("class"); Shiny.setInputValue('last_panel', last_panel, {priority: 'event'}); } else { Shiny.setInputValue('last_panel', '#placeholder', {priority: 'event'}); } Shiny.setInputValue('add_btn_clicked', clicked_id, {priority: 'event'}); })
When a user clicks the added_btn
class of the action link, we get the id of that action link and the action link text. We then send the action link text to R Shiny.setInputValue('header', p, {priority: 'event'})
and remove the action link with $(".added_" + clicked_id).remove()
.
We then check if there is already a visualization in the dashboard. If there is, we get the class from the last visualization on the page and then send either that particular class or #placeholder
to R. We also send the action link id to R.
After we sent these 3 things to R, we will be using the Shiny server to create a visualization with Plotly. However, instead of using the renderUI
function, we will be using the insertUI
function which will be more efficient.
Visualization Helper Function
Before we are going into the server side of things, we will be creating a helper function for the visualizations. We will essentially copy-paste a div from the UI and then add parameters to the function.
We need to make the class in the first div generic, as well as the header, the button id, the button class, and some other parameters that we need to change later. The function that can do that is shown below.
google_analytics_viz <- function(title = NULL, viz = NULL, btn_id, df = NULL, 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) ) ) ) ) ) }
The Shiny Server Side
On the server side, we will have an observer that gets triggered when one clicks the action link. The click of the action link gets sent from JS to R. We then call the google_analytics_viz
function with the suitable parameters. One important part is that the id of the clicked and then deleted action link becomes the id of the visualization and the class of the most outer div will be class_
whatever the id of the clicked action link was.
We then determine if there is already a visualization or not. If not, the visualization will be added after the #placeholder
id that we have yet to implement in the UI, and if there is a visualization already we are going to insert that visualization after the last visualization on the page.
server <- function(input, output) { # 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 ) }) }
The UI Side
For the UI side, we are just going to insert div(id = "placeholder")
to make sure that the visualization is drawn when there is none yet available.
Define UI for application that draws a histogram 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("part_2/www/scripts.js")) )
Putting it All Together
The final app for part 2 looks like this:
library(shiny) library(tidyverse) library(plotly) list.files(here::here("part_2/R")) %>% here::here("part_2/R", .) %>% purrr::walk(~source(.)) # Define UI for application that draws a histogram 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("part_2/www/scripts.js")) ) # Define server logic required to draw a histogram server <- function(input, output) { # 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 ) }) } # Run the application shinyApp(ui = ui, server = server)
- The code for this blog post can be found on my GitHub
Recent Posts
Recent Comments
- Kardiana on The Lasso – R Tutorial (Part 3)
- Pascal Schmidt on RSelenium Tutorial: A Tutorial to Basic Web Scraping With RSelenium
- Pascal Schmidt on Dynamic Tabs, insertTab, and removeTab For More efficient R Shiny Applications
- Gisa on Persistent Data Storage With a MySQL Database in R Shiny – An Example App
- Nicholas on Dynamic Tabs, insertTab, and removeTab For More efficient R Shiny Applications