library( DiagrammeR )
library( shiny )
library( plotly )
library( babynames )
library( shinythemes )
library( gapminder )
library( dplyr )Introduction to Shiny apps in R
What is a web app?
- Updates based on user input/interaction
- Made up of UI & server
How does a web app work?
A web app is a thingy that updates based on user input/interaction. Most web application consist of two parts. The client contains the user interface, that is, buttons and selectors and text boxes and other things that the user can interact with. The server (or backend) is where computation happens, including things like manipulating data and running models.
- Client (User Interface)
- Server (Backend) that carries out computations based on the user interactions
iris shiny app
#plot_kmean()
ui <- fluidPage(
h1( 'K-Means Clustering App' ),
selectInput( 'x', 'Select x', names( iris ), 'Sepal.Length' ),
selectInput( 'y', 'Select y', names( iris ), 'Sepal.Width' ),
numericInput('nb_clusters', 'Select number of clusters', 3 ),
plotly::plotlyOutput( 'kmeans_plot' )
)
server <- function( input, output, session ){
plot_kmeans <- function( data, x, y, nb_clusters ){
k1 <- kmeans(x=data[, 1:4], centers=nb_clusters)
plot( data[ c(x,y) ], col = k1$cluster )}
output$kmeans_plot <- plotly::renderPlotly({
plot_kmeans(iris, input$x, input$y, input$nb_clusters)
})
}
shinyApp( ui = ui, server = server )Shiny skeleton
- Load
shiny - Create the UI with an HTML function
fluidPage()
- Define a custom function to create the server
- ex: input, output & session
- Run the app
shinyApp()
ui <- fluidPage(
"Hello Shiny!"
)
server <- function( input, output, session ){
}
shinyApp( ui = ui, server = server )Ask a Question:
ui <- fluidPage(
textInput( 'name', 'Enter a name:'),
textOutput("q")
)
server <- function( input, output ){
output$q <- renderText( {
paste( "Do you prefer dogs or cats,",
input$name, "?")
})
}
shinyApp( ui = ui, server = server )data( babynames )
ui <- fluidPage(
titlePanel( 'Baby Name Explorer' ),
sidebarLayout( sidebarPanel(
textInput( 'name', 'Enter Name', 'David' ) ),
mainPanel( plotOutput( 'trend' ) ) )
)
server <- function( input, output, session ){
output$trend <- renderPlot( {
data_name <- subset(
babynames, name == input$name
)
ggplot( data_name ) +
geom_line(
aes( x = year, y = prop, color = sex )
)
})
}
shinyApp( ui = ui, server = server )Example Inputs
- slider - slide a pointer to a value on a scale
- select list - a pull down menu allows the user to select between options on a list
- numeric - can enter or increment integer values
- date range - allows user to select a range of dates
ui <- fluidPage(
titlePanel("What's in a Name?"),
# Add select input named "sex" to choose between "M" and "F"
selectInput('sex', 'Select Sex', choices = c("F", "M")),
# CODE BELOW: Add slider input named 'year' to select years (1900 - 2010)
sliderInput( "year", "label", value = 1900, min = 1900, max = 2010 ),
# Add plot output to display top 10 most popular names
plotOutput('plot_top_10_names')
)
server <- function(input, output, session){
# Render plot of top 10 most popular names
output$plot_top_10_names <- renderPlot({
# Get top 10 names by sex and year
top_10_names <- babynames %>%
filter(sex == input$sex) %>%
# MODIFY CODE BELOW: Filter for the selected year
filter(year == input$year ) %>%
top_n(10, prop)
# Plot top 10 names by sex and year
ggplot(top_10_names, aes(x = name, y = prop)) +
geom_col(fill = "#263e63")
})
}
shinyApp( ui = ui, server = server )ui <- fluidPage(
titlePanel("What's in a Name?"),
# CODE BELOW: Add select input named "sex" to choose between "M" and "F"
selectInput( "sex", "Male or Female?", choices = c( "M", "F" ) ),
# Add plot output to display top 10 most popular names
plotOutput('plot_top_10_names')
)
server <- function(input, output, session){
# Render plot of top 10 most popular names
output$plot_top_10_names <- renderPlot({
# Get top 10 names by sex and year
top_10_names <- babynames %>%
# MODIFY CODE BELOW: Filter for the selected sex
filter(sex == input$sex ) %>%
filter(year == 1900) %>%
top_n(10, prop)
# Plot top 10 names by sex and year
ggplot(top_10_names, aes(x = name, y = prop)) +
geom_col(fill = "#263e63")
})
}
shinyApp(ui = ui, server = server)Render & Output Functions
Render functions build outputs in the server based on inputs
Types of render functions:
- renderText()
- renderTable()
- renderImage()
- renderPlot()
- check out the shiny documentation for more
Output functions are in the ui to visualize the result of the render functions in the server Types of output function:
- textOutput()
- tableOutput()
- imageOutput()
- plotOutput()
- dataTableOutput()
ui <- fluidPage(
DT::DTOutput( "babynames_table" )
)
server <- function( input, output ){
output$babynames_table <- DT::renderDT({
babynames %>%
dplyr::sample_frac(.1)
})
}
shinyApp(ui = ui, server = server)ui <- fluidPage(
titlePanel("What's in a Name?"),
# Add select input named "sex" to choose between "M" and "F"
selectInput('sex', 'Select Sex', choices = c("F", "M")),
# Add slider input named "year" to select year between 1900 and 2010
sliderInput('year', 'Select Year', min = 1900, max = 2010, value = 1900),
# CODE BELOW: Add table output named "table_top_10_names"
DT::DTOutput( "table_top_10_names" )
)
server <- function(input, output, session){
# Function to create a data frame of top 10 names by sex and year
top_10_names <- function(){
top_10_names <- babynames %>%
filter(sex == input$sex) %>%
filter(year == input$year) %>%
top_n(10, prop)
}
# CODE BELOW: Render a table output named "table_top_10_names"
output$table_top_10_names <- DT::renderDT({
top_10_names() })
}
shinyApp(ui = ui, server = server)name <- c( 'Kizzy', 'Deneen', 'Royalty', 'Mareli', 'Moesha', 'Marely', 'Kanye',
'Tennille', 'Aitana', 'Kadijah','Shaquille', 'Catina', 'Allisson',
'Emberly', 'Nakia' , 'Jaslene', 'Kyrie', 'Akeelah', 'Zayn', 'Talan' )
sex <- c( 'F', 'F', 'F', 'F', 'F', 'F', 'M', 'F', 'F', 'F', 'M', 'F', 'F',
'F', 'M', 'F', 'M', 'F', 'M', 'M' )
total <- c(2325, 3603, 1806, 1024, 1067, 2577, 1319, 2172, 1625, 1418, 5439,
4178, 2377, 1471, 1991, 2870, 5858, 1331, 3347, 3640 )
max <- c( 1116, 1604, 747, 411, 426, 1004, 508, 769, 564, 486, 1784, 1370, 767,
467, 612, 872, 1774, 403, 988, 1059 )
nb_years <- c( 30, 52, 14, 21, 14, 28, 16, 32, 23, 36, 29, 47, 21, 34, 40, 17, 31, 17, 25, 28 )
trendiness <- c( 0.48, 0.445, 0.414, 0.401, 0.399, 0.390, 0.385, 0.354, 0.347,
0.343, 0.328, 0.328, 0.323, 0.317, 0.307, 0.304, 0.303, 0.303,
0.295, 0.291 )
top_trendy_names <- data.frame( 'name' = name, 'sex' = sex,
'total' = total, 'max' = max,
'nb_years' = nb_years, 'trendiness' = trendiness )
str( top_trendy_names )## 'data.frame': 20 obs. of 6 variables:
## $ name : chr "Kizzy" "Deneen" "Royalty" "Mareli" ...
## $ sex : chr "F" "F" "F" "F" ...
## $ total : num 2325 3603 1806 1024 1067 ...
## $ max : num 1116 1604 747 411 426 ...
## $ nb_years : num 30 52 14 21 14 28 16 32 23 36 ...
## $ trendiness: num 0.48 0.445 0.414 0.401 0.399 0.39 0.385 0.354 0.347 0.343 ...
top_trendy_names A tibble: 20 x 6 name sex total max nb_years trendiness
ui <- fluidPage(
selectInput('name', 'Select Name', top_trendy_names$name),
# CODE BELOW: Add a plotly output named 'plot_trendy_names'
plotly::plotlyOutput( 'plot_trendy_names' )
)
server <- function(input, output, session){
# Function to plot trends in a name
plot_trends <- function(){
babynames %>%
filter(name == input$name) %>%
ggplot(aes(x = year, y = n)) +
geom_col()
}
# CODE BELOW: Render a plotly output named 'plot_trendy_names'
output$plot_trendy_names <- plotly::renderPlotly( {plot_trends()} )
}
shinyApp(ui = ui, server = server)Layouts and Themes
well chosen layout are good for aesthetics!
Default:
ui <- fluidPage(
titlePanel( "histogram" ),
sliderInput( "nb_bins", "# Bins", 5, 10, 5 ),
plotOutput( "hist" )
)
server <- function( input, output, session ){
output$hist <- renderPlot( {
hist( faithful$waiting,
breaks = input$nb_bins,
col = 'pink' )
})
}
shinyApp( ui = ui, server = server )Sidebar layout: inputs to the left, output in the main panel to the right
ui <- fluidPage(
titlePanel( "histogram" ),
sidebarLayout( sidebarPanel( sliderInput( "nb_bins", "# Bins", 5, 10, 5 ) ),
mainPanel( plotOutput( "hist" ) ) )
)
server <- function( input, output, session ){
output$hist <- renderPlot( {
hist( faithful$waiting,
breaks = input$nb_bins,
col = 'pink' )
})
}
shinyApp( ui = ui, server = server )put different plots in different tabs to give each it’s own space
ui <- fluidPage(
titlePanel( 'Histogram' ),
sidebarLayout(
sidebarPanel( sliderInput( 'nb_bins', '# Bins', 5, 10, 5 ) ),
mainPanel(
tabsetPanel(
tabPanel( 'Waiting', plotOutput( 'hist_waiting' ) ),
tabPanel( 'Eruptions', plotOutput( 'hist_eruptions' ) )
))
)
)
server <- function( input, output, session ){
output$hist_waiting <- renderPlot( {
hist(faithful$waiting,
breaks = input$nb_bins,
col = 'pink' )
})
}
shinyApp( ui = ui, server = server )Shiny makes it easy to customize the theme of an app. The UI functions in Shiny make use of Twitter Bootstrap, a popular framework for building web applications. Bootswatch extends Bootstrap by making it really easy to skin an application with minimal code changes.
ui <- fluidPage(
titlePanel( "histogram" ),
#shinythemes::themeSelector(),
theme = shinythemes::shinytheme( 'superhero' ),
sidebarLayout( sidebarPanel( sliderInput( "nb_bins", "# Bins", 5, 10, 5 ) ),
mainPanel( plotOutput( "hist" ) ) )
)
server <- function( input, output, session ){
output$hist <- renderPlot( {
hist( faithful$waiting,
breaks = input$nb_bins,
col = 'pink' )
})
}
shinyApp( ui = ui, server = server )ui <- fluidPage(
# MODIFY CODE BELOW: Wrap in a sidebarLayout
# MODIFY CODE BELOW: Wrap in a sidebarPanel
sidebarLayout( sidebarPanel( selectInput('name', 'Select Name', top_trendy_names$name) ),
# MODIFY CODE BELOW: Wrap in a mainPanel
mainPanel( plotly::plotlyOutput('plot_trendy_names') ,
DT::DTOutput('table_trendy_names') ) )
)
# DO NOT MODIFY
server <- function(input, output, session){
# Function to plot trends in a name
plot_trends <- function(){
babynames %>%
filter(name == input$name) %>%
ggplot(aes(x = year, y = n)) +
geom_col()
}
output$plot_trendy_names <- plotly::renderPlotly({
plot_trends()
})
output$table_trendy_names <- DT::renderDT({
babynames %>%
filter(name == input$name)
})
}
shinyApp(ui = ui, server = server)ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput('name', 'Select Name', top_trendy_names$name)
),
mainPanel(
# MODIFY CODE BLOCK BELOW: Wrap in a tabsetPanel
tabsetPanel(
# MODIFY CODE BELOW: Wrap in a tabPanel providing an appropriate label
tabPanel( 'Plot', plotly::plotlyOutput('plot_trendy_names') ),
# MODIFY CODE BELOW: Wrap in a tabPanel providing an appropriate label
tabPanel( 'Table', DT::DTOutput('table_trendy_names') ) )
)
)
)
server <- function(input, output, session){
# Function to plot trends in a name
plot_trends <- function(){
babynames %>%
filter(name == input$name) %>%
ggplot(aes(x = year, y = n)) +
geom_col()
}
output$plot_trendy_names <- plotly::renderPlotly({
plot_trends()
})
output$table_trendy_names <- DT::renderDT({
babynames %>%
filter(name == input$name)
})
}
shinyApp(ui = ui, server = server)Building apps
Build an app using the gapminder dataset.
Explore Life Expectancy vs. GDP per Capita
Building Shiny apps: 4 steps
- Add inputs (UI)
- Add outputs (server)
- Update layout (UI)
- Update outputs (Server)
ui <- fluidPage(
titlePanel( 'Life Expectation vs. GDP Per Capita' ), #step1
selectInput( 'continent', 'Select Continent', unique( gapminder$continent )), #step1
sliderInput( 'year', 'Select Year', 1952, 2007, 1992, step = 5 ), #step1
plotOutput( 'plot' ), #step2
DT::DTOutput('table') #step2
)
server <- function( input, output, session ){
output$plot <- renderPlot( { #step2 - adding placeholders
ggplot()
})
output$table <- DT::renderDT({ #step2 - adding placeholders
gapminder
})
}
shinyApp( ui = ui, server = server )ui <- fluidPage(
titlePanel( 'Life Expectation vs. GDP Per Capita' ), #step3 - format the UI layout
sidebarLayout(
sidebarPanel(
selectInput( 'continent', 'Select Continent', unique( gapminder$continent )),
sliderInput( 'year', 'Select Year', 1952, 2007, 1992, step = 5 )
),
mainPanel(
tabsetPanel(
tabPanel( 'Plot', plotOutput( 'plot' ) ),
tabPanel( 'Table', DT::DTOutput('table') )
)
)
)
)
server <- function( input, output, session ){ #step4 - update the outputs
output$plot <- renderPlot( { #step2 - adding placeholders
data <- gapminder %>%
filter( year == input$year ) %>%
filter( continent == input$continent )
#print( data )
ggplot( data, aes( x = gdpPercap, y = lifeExp )) +
geom_point() +
ylim( c( 50, 90 ) )
})
output$table <- DT::renderDT({ #step2 - adding placeholders
gapminder %>%
filter( year == input$year ) %>%
filter( continent == input$continent )
})
}
shinyApp( ui = ui, server = server )ui <- fluidPage(
selectInput('greeting', 'Select greeting', choices = c("Hello", "Bonjour")),
textInput( 'name', 'Enter your name', 'Kaelen' ),
textOutput("q")
)
server <- function( input, output ){
output$q <- renderText( {
paste( input$greeting, ',', input$name )
})
}
shinyApp( ui = ui, server = server )ui <- fluidPage(
titlePanel( "Most Popular Names" ),
sidebarLayout( sidebarPanel( selectInput('sex', 'Select sex', choices = c("M", "F")),
sliderInput( "year", "Select year", 1880, 2017, 1 ) ),
mainPanel(
tabsetPanel(
tabPanel( 'Plot', plotOutput( "plot" ) ),
tabPanel( 'Table', DT::DTOutput('table') ) ) ) )
)## Warning: In sliderInput(): `value` should be greater than or equal to `min`
## (value = 1, min = 1880).
server <- function(input, output, session) {
output$plot <- renderPlot( {
top_10_names <- babynames %>%
filter(sex == input$sex) %>%
filter(year == input$year ) %>%
top_n(10, prop)
ggplot(top_10_names, aes(x = name, y = prop)) +
geom_col()
})
output$table <- DT::renderDT({ #step2 - adding placeholders
babynames %>%
filter(sex == input$sex) %>%
filter(year == input$year ) %>%
top_n(10, prop)
})
}
shinyApp(ui = ui, server = server)Reactive Programming
Reactivity 101
Reactive Sources & Reactive Endpoints
Reactive Source - User input that comes through a browser interface, typically
a reactive source can be connected to multiple endpoints and vice versa
Reactive Endpoint - output that typically appears in the browser window, such as a plot or a table of variables
endpoints are notified when the underlying value of sources changes and updates in response to this signal
Reactive Conductor - An intermediate that depends on reactive sources, and/or updates reactive endpoints
Reactive Expression - reactive expressions are lazy & cached
ui <- fluidPage(
titlePanel( 'Greeting' ),
textInput( 'name', 'Enter Name' ), #REACTIVE SOURCE
textOutput( 'greeting' ) #REACTIVE ENDPOINT
)
server <- function( input, output, session ){
output$greeting <- renderText( {
paste( 'Hello', input$name )
})
}
shinyApp( ui = ui, server = server )a reactive conductor
#REACTIVE CONDUCTOR
server <- function( input, output, session ){
output$plot_trendy_names <- plotly::renderPlotly({
babynames %>%
filter( name == input$name ) %>% #the reactive intermediate code gets repeated and reevaluated
ggplot( val_bnames, aes( x = year, y = n )) +
geom_col
})
output$table_trendy_names <- DT::renderDT({
babynames %>%
filter( name == input$name )
})
}a reactive
#REACTIVE EXPRESSION
server <- function( input, output, session ){
rval_babynames <- reactive({
babynames %>%
filter( name == input$name )
})
output$plot_trendy_names <- plotly::renderPlotly({
rval_babynames() %>%
ggplot( val_bnames, aes( x = year, y = n)) +
geom_col()
})
output$table_trendy_names <- DT::renderDT({
rval_babynames()
})
}A reactive expression behaves just like a function, but with two key differences:
- It is lazy, meaning that it is evaluated only when a reactive enpoint calls it
- It is cached, meaning that it is evaluates only when the value of one of its underlying reactive sources changes
ui <- fluidPage(
titlePanel('BMI Calculator'),
theme = shinythemes::shinytheme('cosmo'),
sidebarLayout(
sidebarPanel(
numericInput('height', 'Enter your height in meters', 1.5, 1, 2),
numericInput('weight', 'Enter your weight in Kilograms', 60, 45, 120)
),
mainPanel(
textOutput("bmi"),
textOutput("bmi_range")
)
)
)
server <- function(input, output, session) {
rval_bmi <- reactive({
input$weight/(input$height^2)
})
output$bmi <- renderText({
bmi <- rval_bmi()
paste("Your BMI is", round(bmi, 1))
})
output$bmi_range <- renderText({
bmi <- rval_bmi()
health_status <- cut(bmi,
breaks = c(0, 18.5, 24.9, 29.9, 40),
labels = c('underweight', 'healthy', 'overweight', 'obese')
)
paste("You are", health_status)
})
}
shinyApp(ui, server)Observer vs Reactives
reactive()is for calculating values, without side effects- reactive expressions return values, but observers do not.
observe()is for performing actions, with side effects- observers eagerly response to changes in their dependencies, while reactive expressions are lazy
- side effects observers are primarily useful for their side effects, whereas, reactive expressions must NOT have side effects
ui <- fluidPage(
textInput( 'name', 'Enter Your Name' )
)
server <- function( input, output, session ){
observe({
showNotification(
paste( 'You entered the name', input$name )
)
})
}
shinyApp(ui, server)ui <- fluidPage(
titlePanel('BMI Calculator'),
theme = shinythemes::shinytheme('cosmo'),
sidebarLayout(
sidebarPanel(
numericInput('height', 'Enter your height in meters', 1.5, 1, 2),
numericInput('weight', 'Enter your weight in Kilograms', 60, 45, 120)
),
mainPanel(
textOutput("bmi"),
textOutput("bmi_range")
)
)
)
server <- function(input, output, session) {
rval_bmi <- reactive({
input$weight/(input$height^2)
})
# CODE BELOW: Add a reactive expression rval_bmi_status to
# return health status as underweight etc. based on inputs
rval_bmi_status <- reactive({
cut(rval_bmi(),
breaks = c(0, 18.5, 24.9, 29.9, 40),
labels = c('underweight', 'healthy', 'overweight', 'obese')
)
})
output$bmi <- renderText({
bmi <- rval_bmi()
paste("Your BMI is", round(bmi, 1))
})
output$bmi_status <- renderText({
# MODIFY CODE BELOW: Replace right-hand-side with
# reactive expression rval_bmi_status
bmi_status <- rval_bmi_status()
paste("You are", bmi_status)
})
}
ui <- fluidPage(
titlePanel('BMI Calculator'),
sidebarLayout(
sidebarPanel(
numericInput('height', 'Enter your height in meters', 1.5, 1, 2),
numericInput('weight', 'Enter your weight in Kilograms', 60, 45, 120)
),
mainPanel(
textOutput("bmi"),
textOutput("bmi_status")
)
)
)
shinyApp(ui = ui, server = server)Stop - delay - trigger
The Isolate function allows a reactive expression to read a reactive value without triggering re-execution when it’s value changes. Wrapping a reactive value inside isolate makes it read-only, and does NOT trigger re-execution when it’s value changes
server <- function( input, output, session ){
output$greeting <- renderText( {
paste( isolate(
input$greeting_type
),
input$name, sep = ',' )
})
}There might be a need for more explicit control over the update. Ex: only execute with the press of a button
You can delay the execution of a reactive expression by placing it inside eventReactive(), and specifying an event in response to which it should execute the expression.
server <- function( input, output, session ){
rv_greeting <- eventReactive( input$show_greeting, {
paste( 'Hello', input$name )
})
output$greeting <- renderText( {
rv_greeting()
})
}Triggering actions. Unlike eventReactive(), observeEvent() is used only for it’s side effects and does not return any value
server <- function( input, output, session ){
observeEvent( input$show_greeting, {
showModal( modalDialog( paste( "Hello", input$name )))
})
}server <- function(input, output, session) {
rval_bmi <- reactive({
input$weight/(input$height^2)
})
output$bmi <- renderText({
bmi <- rval_bmi()
# MODIFY CODE BELOW:
# Use isolate to stop output from updating when name changes.
paste("Hi", isolate({input$name}), ". Your BMI is", round(bmi, 1))
})
}
ui <- fluidPage(
titlePanel('BMI Calculator'),
sidebarLayout(
sidebarPanel(
textInput('name', 'Enter your name'),
numericInput('height', 'Enter your height (in m)', 1.5, 1, 2, step = 0.1),
numericInput('weight', 'Enter your weight (in Kg)', 60, 45, 120)
),
mainPanel(
textOutput("bmi")
)
)
)
shinyApp(ui = ui, server = server)server <- function(input, output, session) {
# rval_bmi <- eventReactive(
# input$show_bmi, {
# bmi <- input$weight/(input$height^2)
# paste("Hi", input$name, ". Your BMI is", round(bmi, 1))
# })
# output$bmi <- renderText({
# rval_bmi()
# })
# }
rval_bmi <- eventReactive(input$show_bmi, {
input$weight/(input$height^2)
})
output$bmi <- renderText({
bmi <- rval_bmi()
paste("Hi", input$name, ". Your BMI is", round(bmi, 1))
})
}
ui <- fluidPage(
titlePanel('BMI Calculator'),
sidebarLayout(
sidebarPanel(
textInput('name', 'Enter your name'),
numericInput('height', 'Enter height (in m)', 1.5, 1, 2, step = 0.1),
numericInput('weight', 'Enter weight (in Kg)', 60, 45, 120),
actionButton("show_bmi", "Show BMI")
),
mainPanel(
textOutput("bmi")
)
)
)
shinyApp(ui = ui, server = server)bmi_help_text <- "Body Mass Index is a simple calculation using a person's height and weight. The formula is BMI = kg/m2 where kg is a person's weight in kilograms and m2 is their height in metres squared. A BMI of 25.0 or more is overweight, while the healthy range is 18.5 to 24.9."
server <- function(input, output, session) {
# MODIFY CODE BELOW: Wrap in observeEvent() so the help text
# is displayed when a user clicks on the Help button.
observeEvent( input$show_help, {
# Display a modal dialog with bmi_help_text
# MODIFY CODE BELOW: Uncomment code
showModal(modalDialog(bmi_help_text))
})
rv_bmi <- eventReactive(input$show_bmi, {
input$weight/(input$height^2)
})
output$bmi <- renderText({
bmi <- rv_bmi()
paste("Hi", input$name, ". Your BMI is", round(bmi, 1))
})
}
ui <- fluidPage(
titlePanel('BMI Calculator'),
sidebarLayout(
sidebarPanel(
textInput('name', 'Enter your name'),
numericInput('height', 'Enter your height in meters', 1.5, 1, 2),
numericInput('weight', 'Enter your weight in Kilograms', 60, 45, 120),
actionButton("show_bmi", "Show BMI"),
# CODE BELOW: Add an action button named "show_help"
actionButton("show_help", "Help")
),
mainPanel(
textOutput("bmi")
)
)
)
shinyApp(ui = ui, server = server)Applying Reactivity Concepts
Reactives and Observers
- Reactive sources are accessible through any input$x
- Reactive condictors are good for slow or expensive calculations, and are placed between sources and endpoints
- Reactive endpoints are accessible through any output$y, and are observers, primarily used for their side effects, and not directly to calculate things
Stop, Delay, Trigger
- Stop with
isolate() - Delay with
eventReactive() - Trigger with
observeEvent()
Convert Height from inches to centimeters
server <- function(input, output, session) {
# MODIFY CODE BELOW: Delay the height calculation until
# the show button is pressed
rval_height_cm <- eventReactive( input$show_height_cm, {
input$height * 2.54
})
output$height_cm <- renderText({
height_cm <- rval_height_cm()
paste("Your height in centimeters is", height_cm, "cm")
})
}
ui <- fluidPage(
titlePanel("Inches to Centimeters Conversion"),
sidebarLayout(
sidebarPanel(
numericInput("height", "Height (in)", 60),
actionButton("show_height_cm", "Show height in cm")
),
mainPanel(
textOutput("height_cm")
)
)
)
shinyApp(ui = ui, server = server)