app.R
app.R
is the (single) script that contains a shiny app.
app.R
consists of three components:
- A user interface object (traditionally called
ui
)
- A server function (traditionally called
server
)
- Calling
shinyApp(ui = ui, server = server)
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.
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)
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:
-
actionButton
-
checkboxGroupInput
-
checkboxInput
-
dateInput
-
dateRangeInput
-
fileInput
-
helpText
-
numericInput
-
radioButtons
-
selectInput
-
sliderInput
-
submitButton
-
textButton
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.
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());
});
}
);
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":[]}
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');
}
);
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)
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)
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
)
);
});
}
);
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) {}
);
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.
function htmlTemplate
...