2022-03-03

Reviewing my First Shiny Project (1/n) – Buttons

My first serious Shiny project is finished: 4517 lines of code (without comments)! Now I’m taking the time to go through the code again and reflect. What has turned out well and can be continued in the future? What are problem areas and need to be reworked?

I share my thoughts here in a series of articles, also to sort them out and not to forget them. This first article is about buttons.

Vertical alignment

Often my button is placed to the right of an input box, slider or similar. The other input elements often have a label, the button does not and is then vertically shifted:

vertically shifted button

On stackoverflow I asked how to fix this and quickly got a solution:

shiny::column(2, 
    shiny::actionButton(ns("dbconnect"), "Connect!"),
    style = "margin-top:25px;" ## <-- !
)

Warnings and Error Messages in Shiny

I want the code that actually does something (hereafter: working code) to be separate from Shiny. That means I don’t want any calls to showNotification in my working code. However, I still want to use the classic signal functions of R – message, warning and stop – in my working functions.

On Stackoverflow I found a solution for this and defined it as the function exec_safely. The function is stored here. It is a decorator, where an expression is executed with tryCatch and withCallingHandlers.

Now when I write an event handler for a button, I use exec_safely:

rv <- shiny::reactiveValues()
shiny::observeEvent(input$btn, exec_safely(session,{
  rv[["log_x"]] <- log(as.character(input$x))
  })
)

The sample app demonstrates the behaviour. To do this, the code at the link above must be read in, e.g. via source("https://pastebin.com/3RURswkd"). Then both the functions exec_safely and demo_exec_safely are defined. The app is started via demo_exec_safely().

Negative values should produce a warning and non-numeric values an error and display in the web interface.

Plot! Button

In my app there are several tabs that visualise data from a database. The visualisation is parameterised. The parameters either concern the data (e.g. a time period) or are display options (e.g. the font size). Accordingly, the renderPlot function can be understood as a function with two arguments: do_plot(dset, plot_options).

It has proved useful to include a “Plot!” button that recalculates the dset variable:

rv <- shiny::reactiveValues()
shiny::observeEvent(input$btn, {
  rv[["dset"]] <- calc_dset() 
})

The function calc_dset is often a reactive expression depending on one or more interactive input parameters and time-consuming.

At the above link, a function demo_confirm_plot is also defined, which demonstrates both the confirmation dialogue and the Plot! button.

Note that the parameter n can be changed without re-plotting. Re-plotting only occurs after the “Plot!” button is pressed. This allows the user to set the data parameters at their leisure (especially if there are multiple parameters) and decide for themselves when to perform the elaborate operation.

If, on the other hand, the parameter ‘col’ is changed, the plot is immediately regenerated, because no data has to be recalculated.

Summary

Regarding the implementation of buttons in my first larger Shiny project, I am satisfied. There are still some code places where I proceeded differently than described here, this still needs to be adjusted and updated.

Share your thoughts on this article on Twitter!