Lab 10: Shiny Apps

Interactive Apps

Today, we’ll introduce Shiny, which allows us to easily produce interactive webpages with R! Make sure to install and load it:

install.packages("shiny")
library(shiny)

Drawing, Updating Text?

Let’s say we wanted to write an extremely simple interactive application:

The user types some text into a textbox and what they’ve entered is displayed to them.

Something like this:

Updates?

There’s a piece of data, the input string, on which the printed text depends.

  • When and how do we update the printed text?
  • Said another way, when do we draw the printed text?

We need some system of answering these questions.

Draw Constantly!

We could redraw the text from the input each frame.

repeat {
    val <- textbox_value() # get value in text box
    draw(val)
}

Problem: this is very slow.

Draw on Change

We should only update the text when the input has changed.

last_val <- NA
repeat {
    val <- textbox_value() # get value in textbox
    if (val != last_val) {
        draw(val)
        last_val <- val
    }
}

Problem: this is difficult to write and manage.

Reactivity

It’d be nice if we could set a variable and specify what to do when it changes without having to track the changes ourselves.

update <- function(val) {
    draw(val)
}

val <- on_change(NA, update) # starts as NA, calls update when changed

This, at a high level, is how Shiny works. Shiny is reactive, which makes our lives much easier.

Let’s explore Shiny and see what this means.

Shiny Overview

Your Shiny application will consist of two objects.

UI (User Interface)

Here you define the components that make up the webpage. Some examples:

  • Textboxes
  • Buttons
  • Regular text
  • Sliders

You give each a name.

Shiny Overview

Your Shiny application will consist of two objects.

Server

Here you define the rules for how the webpage will react to changes in the component’s values.

  • You refer to the UI components by the names you set in the UI object.

Example (UI)

In the UI object, you define the components of the webpage.

  • There are functions for each component (e.g., textInput).
  • There are input components and output components.
  • The first argument of each component will become the name of the component (this is a string). You will later use this name in the server code.
  • Some components may have additional arguments to define its appearance and behavior. For example, textInput takes a prompt.
  • See here for a list of all the components.
ui <- fluidPage(
    textInput(           # input component:
        "text_input",         # - name
        "Feed me some text!"  # - prompt
    ),
    textOutput(          # output component:
        "text_output"         # - name
    )
)

server <- function(input, output, session) {
    output$text_output <- renderText({
        if (nChars(input$text_input) > 0) {
            input$text_input
        } else {
            "I'm hungry!"
        }
    })
}

shinyApp(ui, server)

You structure the webpage by the order of nested function calls. There are additional components just for structuring the components (i.e., layout components).

ui <- fluidPage(     # This is the top level layout component.
    column(          # This defines a column in the webpage.
        textInput(), 
        textOutput(),
    ),
    column(
        textInput(),
        textOutput(),
   )
)

Example (Server)

The server is a function that takes input, output, and a session (don’t worry about the last).

  • The input and output objects are both lists.
  • These lists will have the named objects you defined in the UI. For example, we named a textInput object "text_input" so access it with input$text_input.
  • You will need to assign to each value in the object list a call to a rendering function (e.g., renderText, renderPlot).
ui <- fluidPage(
    textInput(           # input component:
        "text_input",         # - name
        "Feed me some text!"  # - prompt
    ),
    textOutput(          # output component:
        "text_output"         # - name
    )
)

server <- function(input, output, session) {
    output$text_output <- renderText({
        if (nchar(input$text_input) > 0) {
            input$text_input
        } else {
            "I'm hungry!"
        }
    })
}

shinyApp(ui, server)

Note that output$text_output depends on input$text_input.

server <- function(input, output, session) {
    output$text_output <- renderText({
        if (nchar(input$text_input) > 0) {
            input$text_input
        } else {
            "I'm hungry!"
        }
    })
}

Shiny will track this dependency for us and will update the output component whenever the input changes!

Example (Running the App)

Finally, we run the application by calling shinyApp(ui, server).

ui <- fluidPage(
    textInput(           # input component:
        "text_input",         # - name
        "Feed me some text!"  # - prompt
    ),
    textOutput(          # output component:
        "text_output"         # - name
    )
)

server <- function(input, output, session) {
    output$text_output <- renderText({
        if (nchar(input$text_input) > 0) {
            input$text_input
        } else {
            "I'm hungry!"
        }
    })
}

shinyApp(ui, server)

More on Reactivity

The value output$text_output was made reactive as we assigned a renderText call to it.

The dependencies and updates are then managed by Shiny.

output$text_output <- renderText({
    if (nchar(input$text_input) > 0) {
            input$text_input
    }
    else {
        "I'm hungry!"
    }
})

What if we want some variable to react to changes in input?

Reactive Variables

To do this, you instead wrap the code that determines the value in a call to the reactive function.

server <- function(input, output, session) {
    text <- reactive({
        if (nchar(input$text_input) > 0) {
            input$text_input
        }
        else {
            "I'm hungry"
        }
    })

    output$text_output <- renderText({
        text() # Call the variable like a function to get its value.
    })

    # Now we can use this value in multiple components!
    output$text_output2 <- renderText({
        paste0("You entered: ", text())
    })
    
}

Here, when text’s dependents change, all of the outputs that depend on text will also update.