Search notes:

R package: shiny

shiny allows to build interactive web apps with R.

app.R

app.R is the (single) script that contains a shiny app.
app.R consists of three components:
library(shiny)

ui     <- fluidPage (
            …
          );

server <- function(
                input ,  # input stores the current values of all widgets
                output
          ) {

            # This function is called when a user starts
            # using a shiny app.
            …
          }

shinyApp(ui = ui, server = server)
app.R is usually stored in its own directory and executed with
runApp('/path/to/the/directory');
An app can be executed in show case mode in which case the code of the app is shown and the executed code highlighted.
runApp('/path/to/the/directory', display.mode='showcase');
Alternatively, show case mode can also be enabled within the DESCRIPTION file.

fluidPage

fluidPage() is called with calls to elements that define the user interface. With fluidPage(), the layout of the elements is adjusted to the dimensions of the browser.
Some often seen elements in the call of fluidPage are
ui <- fluidPage(

        titlePanel('Some Title goes here'),

        sidebarLayout(
          # position = 'right',
            sidebarPanel(…)   ,
            mainPanel   (…)
        )
)
An alternative to using fluidPage is to use fluidRow and column for a tabular interface.

sidebarLayout

sidebarLayout takes at least these two arguments:

*Panel functions

*Panel() functions are used to add content to the user interface.

*Output

*Output() functions display R objects on the user interface.
These functions are placed into the calls to the *Panel() functions.
When calling a *Output function, one argument is required: the name of the reactive element.
The name given then becomes a slot(?) (entry) in the output variable, for example output$foo for textOutput('foo').
Each of these entries should correspond to a render* function whose return value is assigned to the output$… slot.
Some (or all?) of the *Output functions are

HTML Elements

HTML elements can be placed where *Output elements can be placed as well.
#
#  vi: foldmarker={{{,}}} foldmethod=marker
#

ui <- fluidPage( # {{{

  titlePanel("HTML Elements"),

  sidebarLayout ( # {{{
  
    sidebarPanel( # {{{

      h1("H1 in sidebarPanel")

    ), # }}}

    mainPanel( # {{{

      h1("H1 in mainPanel"),

      div("A div with red letters", style="color:red")

    ) # }}}

  ) # }}}

); # }}}

server <- function(input, output) {};

shinyApp(ui, server)
Github repository about-r, path: /packages/shiny/htmlElements/app.R

HTML functions just generate text

shiny's HTML functions just generated HTML test:
library(shiny)

div(class='tq84', 'Hello world')
# <div class="tq84">Hello world</div>

textInput('id_text', 'Text label')
# <div class="form-group shiny-input-container">
#   <label for="id_text">Text label</label>
#   <input id="id_text" type="text" class="form-control" value=""/>
# </div>

sidebarPanel(div('first div'), div('second div'))
# <div class="col-sm-4">
#   <form class="well">
#     <div>first div</div>
#     <div>second div</div>
#   </form>
# </div>

img

img(src='something.png', width=200)
The specified image is searched in a directory named www beneath the application folder.

Input Widgets

Widgets get input from a user.
Most widgets are created with a *Input function, some are created with *Button or *Buttons function:
Each widget function take at least two arguments: the name of the widget and a label (which can be '').
The value of the widget is stored within the server function in input$widgetName.

sliderInput

The sliderInput widget allows to enter a single value or a range. The behaviour depends on the value of the parameter value. If it is a two element vector, a range is expected.
Choose a single value (within a given range):
        sliderInput(inputId = 'foo',
                    label   = 'Foo',
                    min     =  18  ,
                    max     =  72  ,
                    value   =  44
        )
Choose a sub range within a given range:
        sliderInput(inputId = 'bar_range',
                    label   = 'bar Range',
                    min     =  0         ,
                    max     =  100       ,
                    value   = c(10, 90)
        )

reactive

All inputs are reactive.

render* functions

The render* functions processes the value of an R expression and makes it suitable for displaying in an element that was placed on the user interface with one of the *Output functions.
The expression can consist of many lines of code.
The expressions need to be encapsulated into curly braces ({ … }).
The expressions in render* functions are executed when a user changes the value of a widget.
Some (all) functions are

Close button

#
#  Found at
#
#    https://github.com/daattali/advanced-shiny/tree/master/close-window
#      via
#    https://stackoverflow.com/a/43864498/180275
#

library(shinyjs)

jscode <- "shinyjs.closeWindow = function() { window.close(); }"

ui <- fluidPage(
  useShinyjs(),
  extendShinyjs(text = jscode, functions = c("closeWindow")),
  actionButton("close", "Close window")
)

server <- function(input, output, session) {
  observeEvent(input$close, {
    js$closeWindow()
    stopApp()
  })
}

shinyApp(ui, server)
Github repository about-r, path: /packages/shiny/closeButton/app.R

Exiting app when browser closes

#
#  Found at
#    https://github.com/daattali/advanced-shiny/blob/master/auto-kill-app/app.R
#
library(shiny)

ui <- fluidPage()

server <- function(input, output, session) {
  session$onSessionEnded(stopApp)
}

shinyApp(ui, server)
Github repository about-r, path: /packages/shiny/exitWhenBrowserCloses/app.R

Debugging

Printing to stderr

R function: cat(file=stderr(), "input$foo = ", $input$foo, "\n");
It's recommended to write to stderr because output to stdout might be captured because in some cases (for example renderPrint) might be captured by a capture.output.

browser()

The call to the (wierdly named) browser() statement stops execution and allows to inspect variables etc.
After inspecting them, execution can be resumed with cont (or c for short?).

Showing the reactive log

options(shiny.reactlog = TRUE);
runApp('/path/to/app');
After setting this option, the reactive log can be shown by pressing ctrl+F3 in the browser window that hosts the app.
library(shiny)

options(shiny.reactlog = TRUE);

shinyApp(
   ui = fluidPage (

     actionButton(
        inputId = 'go',
        label   = 'Get time'
     ),

     verbatimTextOutput(outputId = 'tm')
   ),

   server = function(input, output, session) {

     observeEvent(input$go, {
       output$tm <- renderText(Sys.time());
     });

   }

);
Github repository temp-shiny, path: /options/shiny.reactlog/app.R
In order for this functionality to run, the package reactlog needs to be installed.

Calling an error function

The shiny.error option allows to specify a function to be called when an error occurs.
options(shiny.error = browser);

Architecture

Shiny implements a client-server paradigm: the server being an R process and the client a (web) browser.
The client is connected to the server by a WebSocket.
As soon as the client detects a state change (usually because the user changed or entered an input), the client notifies the server on the channel that is established with the WebSocket.
shiny uses JSON to send data back and forth.

Intercepting traffic between client and server

The data that is exchanged between the client and server can be monitored by setting options(shiny.trace = TRUE).
library(shiny)

options(shiny.trace = TRUE);

shinyApp(
   ui = fluidPage (

     actionButton(
        inputId = 'go',
        label   = 'Get time'
     ),

     verbatimTextOutput(outputId = 'tm')
   ),

   server = function(input, output, session) {

     observeEvent(input$go, {
       output$tm <- renderText(Sys.time());
     });

   }

);
Github repository temp-shiny, path: /options/shiny.trace/app.R
When the browser connects to the server, something like the following might be exchanged. (SEND and RECV are from the perspective of the server).
SEND {"config":{"workerId":"","sessionId":"579c95df425f54e24c22ee1bc97230aa","user":null}}
RECV {"method":"init","data":{"go:shiny.action":0,".clientdata_output_tm_hidden":false, … }}
SEND {"busy":"busy"}
SEND {"busy":"idle"}
SEND {"errors":[],"values":[],"inputMessages":[]}
When the user presses the button, something like to following will be seen on the channel:
RECV {"method":"update","data":{"go:shiny.action":1}}
SEND {"busy":"busy"}
SEND {"recalculating":{"name":"tm","status":"recalculating"}}
SEND {"recalculating":{"name":"tm","status":"recalculated"}}
SEND {"busy":"idle"}
SEND {"errors":[],"values":{"tm":"1563524235"},"inputMessages":[]}

DESCRIPTION

A file named DESCRIPTION might be placed into the application folder, for example with the following content.
Title: Some name for the shiny-app
Author: Joe
DisplayMode: Showcase
Type: Shiny

TODO

Shiny app skeleton

A Shiny App object is created or run with shinyApp(…).
If the object is created in the console and not assigned to a variable, it is passed to the print(…) function which in turn will run the application.
A Shiny App object can be explicitly run by passing it to the runApp(…) function.
library(shiny)

shinyApp(
 #
 # ui     : how to GUI looks like
 #
   ui     = fluidPage (
              verbatimTextOutput(outputId = 'text')
            ),

 #
 # server : Prepare the data to be shown in the ui:
 #
   server = function(input, output, session) {
               output$text <- renderText('Some text');
            }

);
Github repository temp-shiny, path: /skeleton/App.R

sidebarLayout()

ui <- fluidPage(

  titlePanel('Title'),

    sidebarLayout(

      sidebarPanel(
         tags$div('In a', tags$i('sidebar layout'), 'the sidebar is usually used to gather the input-values from a user.'),
         tags$div('This is the place where the input controls (such as', tags$code('selectInput()'),') should be placed.'),
         tags$p(),

         selectInput(inputId = 'input_one',
            label   = 'Choose Wisely',
            choices = c('foo', 'bar', 'baz')
         ),
         selectInput(inputId = 'input_two',
            label   = 'Choose again',
            choices = c('foo', 'bar', 'baz')
         )
      ),

      mainPanel(
        verbatimTextOutput('mainPanelText')
      ),

      position = 'left',
      fluid    =  TRUE

    )
);

server = function(input, output, session) {
               output$mainPanelText <- renderText(
'The main Panel is usually used to display the
data that is calculated in the server function.');
};

shinyApp(ui, server)
Github repository temp-shiny, path: /ui/layout/sidebar/App.R

Various ways to get input from a user

shiny comes with a few widgets (bootstrap?) to get input (data) from a user.
These widgets need to be placed in the ui side of an app.
ui <- fluidPage(

  titlePanel('Various ways to gather input from the user'),

  sidebarLayout(

    sidebarPanel(

      actionButton(
         inputId            = 'i_actionButton',
         label              = 'actionButton',
      #  icon               =  …,
      #  width              =  … 
      ),

      checkboxInput(
         inputId            = 'i_checkboxInput',
         label              = 'checkboxInput',
         value              =  TRUE
      ),

      checkboxGroupInput(
         inputId            = 'i_checkboxGroupInput',
         label              = 'checkboxGroupInput',
         choices            = c('foo', 'bar', 'baz'),
         selected           = c('foo',        'baz')
      ),

      dateInput(
         inputId            = 'i_dateInput',
         label              = 'dateInput',
         value              = '2019-08-28',
         min                = '2019-01-01',
         max                = '2019-12-31',
         format             = 'yyyy-mm-dd',  # This is the default format
         startview          = 'month',       # This is the default startView
         weekstart          =  0,            # This is the default Weekstart
      #  language           = 'en',
      #  width              =  …,
      #  autoclose          =  TRUE
      #  datesdisabled      =  NULL,
      #  daysofweekdisabled =  NULL
      ),

      dateRangeInput(
         inputId            = 'i_dateRangeInput',
         label              = 'dateRangeInput',
         min                = '2017-01-01',
         max                = '2019-12-31',
         start              = '2018-02-04',
         end                = '2018-02-09'
      ),

      fileInput(
         inputId            = 'i_fileInput',
         label              = 'fileInput',
         multiple           =  FALSE,
         accept             = c('text/csv', 'text/comma-separated-values,text/plain', '.csv')
      ),

      numericInput(
         inputId            = 'i_numericInput',
         label              = 'numericInput',
         value              =  10
      ),

      passwordInput(
        inputId             = 'i_passwordInput',
        label               = 'passwordInput',
        value               = 'mySecretGarden',
      # width               =  NULL,
        placeholder         = 'Hint hint hint'
      ),

      radioButtons(
         inputId            = 'i_radioButtons',
         label              = 'radioButtons',
         choices            = c('foo', 'bar', 'baz'),
         inline             =  TRUE
      ),

      selectInput(
         inputId            = 'i_selectInput',
         label              = 'selectInput',
         choices            = c('foo', 'bar', 'baz')
      ),

      sliderInput(
         inputId            = 'i_sliderInput',
         label              = 'sliderInput',
         min                =  1,
         max                =  50,
         value              =  30
      ),

      sliderInput(
         inputId            = 'i_sliderInput_range',
         label              = 'sliderInput (with range)',
         min                =  1,
         max                =  50,
         value              = c(15, 25)
      ),

      submitButton(  # NOTE: no inputId parameter.
         text               = 'submitButton',
      #  icon               =  …,
      #  width              =  …
      ),

      textAreaInput(
         inputId            = 'i_textAreaInput',
         label              = 'textAreaInput',
         value              = "line one\nline two\nline three",
      #  width              =  …
         height             = '80px',
      #  cols               =  …,
      #  rows               =  …,
         placeholder        = 'Hint hint hint',
         resize             = 'horizontal' # or both, or none, or vertical
      ),

      textInput   (
         inputId            = 'i_textInput',
         label              = 'textInput',
         value              = 'hello world'
      )

    ),

    mainPanel(
      tags$div('Choose whatever you like, nothing will ever change here')
    )
  )
)

server = function(input, output, session) {};

shinyApp(ui, server)
Github repository temp-shiny, path: /ui/XYZinput/app.R

Hiding and showing panels

conditionalPanel(…) allows to show or hide a panel depending on the value of a JavaScript expression which in turn might be set using the output$… construct.
ui <- fluidPage(

  selectInput(
     inputId = 'panel',
     label   = 'Choose Panel',
     choices = c('Panel one'   = 'pnl_1',
                 'Panel two'   = 'pnl_2',
                 'Panel three' = 'pnl_3'
                )
  ),

  conditionalPanel(
  #
  #  conditionalPanel() wraps a set of UI elements that
  #  then can be dynamically shown/hidden depending on
  #  the value of a JavaScript expression.
  #
  #  condition:
  #      The JavaScript expression that determines
  #      whether the widgets are shown.
  #      The JavaScript objects(?) input and output
  #      reflect the current respective values.
  #
     condition    = 'output.show_panel == 1',
     tags$div('This is the first Panel')
  ),

  conditionalPanel(
     condition    = 'output.show_panel == 2',
     tags$div('This is the second Panel')
  ),

  conditionalPanel(
     condition    = 'output.show_panel == 3',
     tags$div('This is the third Panel')
  )

);

server <- function(input, output, session) {

  output$show_panel <- reactive({

    switch(input$panel,
           'pnl_1'    = 1,
           'pnl_2'    = 2,
           'pnl_3'    = 3
    )

  });

  outputOptions(output, 'show_panel', suspendWhenHidden = FALSE)
};

shinyApp(ui, server)

uiOutput / renderUI

With the uiOutput / renderUI combination, it's possible to dynamically create input widgets.
library(shiny)

shinyApp(

   ui = fluidPage (
      uiOutput('dynamic_controls')
   ),

   server = function(input, output, session) {
      output$dynamic_controls <- renderUI({

      tagList(
         numericInput(
           inputId = 'i_num',
           label   = 'Enter a number',
           value   =  10
         ),
         numericInput(
           inputId = 'i_num_2',
           label   = 'Enter another number',
           value   =  10
         )
        );

      });
   }

);
Github repository temp-shiny, path: /ui/XYZoutput/ui/app.R

HTML tags

Text can be rendered with different HTML tags. The available tags are found as functions in the tags list.
The following example uses lapply() on names(tags) to produce an unordered list of tags.
library(shiny)

shinyApp(
   ui     = fluidPage (
     #
     # Show all defined HTML tags that can be 
     # used in shiny in an unordered list:
     #
       tags$ul(
         lapply(names(tags), tags$li)
       )
   ),

   server = function(input, output, session) {}

);
Github repository temp-shiny, path: /ui/html/tags/App.R

Reactive functions

Some functions are reactive: render…(), …?
renderPlot is reactive: it is called whenever the inputs (input$…) change.
Reactive elements must be accessed within reactive functions. If accessed outside of a reactive context, the error Operation not allowed without an active reactive context is thrown.
Apparently, a reactive context can also be established with the function reactive({ … }).

Showcase mode

shiny::runApp(display.mode='showcase');
Show case mose can also be enabled in the DESCRIPTION file by adding the following line:
DisplayMode: Showcase

Shiny Server

Shiny Server is a Linux(?) platform to publish mutliple shiny applications on a single machine.

Log files

The server writes log messages to /var/log/shiny-server.log and /var/log/shiny-server/*.

function htmlTemplate

...

See also

R packages shinydashboard, shinyjs
reactlog allows to visually investigate the dependencies of shiny applications.
R packages

Index