Simplifying Parts Of A Shiny App by Creating Functions

[This article was first published on R, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
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.

To leave a comment for the author, please follow the link and comment on their blog: R.

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.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)