Mastering Shiny — 4.8 Exercise 1: Next and Back Button

Below is the 4th exercise in book Mastering Shiny, Chapter 4: Case study: ER injuries.

“Provide a way to step through every narrative systematically with forward and backward buttons.Advanced: Make the list of narratives “circular” so that advancing forward from the last narrative takes you to the first.”

Collin Berke provided a solution, where the %% was used for the circular purpose. However, employing %% is a little tricky in this exercise. Thus, I use if else in both Next and Previous arrows. Here is the code.

library(shiny)
library(ggplot2)
library(vroom)
library(tidyverse)

# set your own directory
setwd('../../../data')


injuries <- vroom::vroom("neiss/injuries.tsv.gz")

population <- vroom::vroom("neiss/population.tsv")

products <- vroom::vroom("neiss/products.tsv")

prod_codes <- setNames(products$prod_code, products$title)

count_top <- function(df, var, n = 5) {
  df %>%
    mutate({{ var }} := fct_lump(fct_infreq({{ var }}), n = n)) %>%
    group_by({{ var }}) %>%
    summarise(n = as.integer(sum(weight)))
}

ui <- fluidPage(
  fluidRow(
    column(8,
           selectInput("code", "Product",
                       choices = setNames(products$prod_code, products$title),
                       width = "100%"
           )
    ),
    column(2, selectInput("y", "Y axis", c("rate", "count"))),
    column(2, numericInput("num_row", "Number of rows", 5, min = 1, max = 15))
  ),
    fluidRow(
      column(4, tableOutput("diag")),
      column(4, tableOutput("body_part")),
      column(4, tableOutput("location"))
    ),
    fluidRow(
      column(12, plotOutput("age_sex")),
      
    ),
  fluidRow(
    column(2, span("Narrative Display:", style = "font-weight: bold")),
    column(1, actionButton(inputId ="Previous", label = icon("arrow-left"))),
    column(1, actionButton(inputId ="Next", label = icon("arrow-right"))),
    column(8, textOutput("narrative"))
  )
  
)

server <- function(input, output, session) {
  
  # use the last 6 records to test narrative
  selected <- reactive(injuries %>% filter(prod_code == input$code) %>% slice_tail(n=6))
  
  n_row <- reactive(input$num_row) # number of rows displayed in tables
  
  output$diag <- renderTable(count_top(selected(), diag, n = n_row()), width = "100%")
  output$body_part <- renderTable(count_top(selected(), body_part, n = n_row()), width = "100%")
  output$location <- renderTable(count_top(selected(), location, n = n_row()), width = "100%")
  
  summary <- reactive({
    selected() %>%
      count(age, sex, wt = weight) %>%
      left_join(population, by = c("age", "sex")) %>%
      mutate(rate = n / population * 1e4)
  })
  
  output$age_sex <- renderPlot({
    if (input$y == "count") {
      summary() %>%
        ggplot(aes(age, n, colour = sex)) +
        geom_line() +
        labs(y = "Estimated number of injuries")
    } else {
      summary() %>%
        ggplot(aes(age, rate, colour = sex)) +
        geom_line(na.rm = TRUE) +
        labs(y = "Injuries per 10,000 people")
    }
  }, res = 96)

  ### narrative
  num_narr <- reactive(
    length(selected()$narrative)
  )
  
  # a reactive value that can be easily changed later (in events)
  # ref: https://stackoverflow.com/questions/42183161/r-shiny-how-to-change-values-in-a-reactivevalues-object
  i <- reactiveValues(tmp=1)

  # reset i to 1 if code is changed by user
  # ref: https://www.collinberke.com/post/shiny-series-implementing-a-next-and-back-button/
  observeEvent(input$code, {
    i$tmp <- 1
  })
  
  output$narrative <- renderText({
    selected()$narrative[1]
  })
  
  observeEvent(input$Next, {
    
    i$tmp <- i$tmp + 1
    
    if(i$tmp <= num_narr()){
      output$narrative <- renderText({
        selected()$narrative[i$tmp]
      })
    } else{
      i$tmp <- 1
      output$narrative <- renderText({
        selected()$narrative[1]
      })
    }
  })
  
 observeEvent(input$Previous, {
   i$tmp <- i$tmp - 1
   
   if(i$tmp > 0){
     output$narrative <- renderText({
       selected()$narrative[i$tmp]
     })
   } else{
     i$tmp <- num_narr()
     output$narrative <- renderText({
       selected()$narrative[num_narr()]
     })
   }
 })
 
}

shinyApp(ui, server)

One thought on “Mastering Shiny — 4.8 Exercise 1: Next and Back Button

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s