Yozef
Yozef

Reputation: 125

updating of Shiny tabs

I'm trying to build a Shiny app with several tabs. One of the tabs serves for the user to choose the underlying data-set and I would like this data to be displayed in nice graphs in another. (I am keeping the underlying data in files of a max. size that the user can investigate one at a time as I fear that otherwise the Shiny app might become insufferably slow or even crash). I'm struggling updating the variable holding the underlying data, however, as illustrated in the much simplified reproducible example below:

library(shiny)
library(shinyWidgets)
library(dplyr)
library(ggplot2)

dataFrameA <- data.frame(
  company = c("A", "B", "C", "D"), 
  revenue = runif(4, min = 0, max = 1000000)
)

dataFrameB <- data.frame(
  company = c("E", "F", "G", "H"), 
  revenue = runif(4, min = 0, max = 1000000)
)

usedDataFrame <- dataFrameA

ui <- fluidPage(
  verticalTabsetPanel(
    verticalTabPanel(
      title = "graph", icon = icon("chart-bar", "fa-1x", lib = "font-awesome"),
      box_height = "120px", 
      plotOutput(outputId = "barChart")
    ), 
    verticalTabPanel(
      title = "data", icon = icon("database", "fa-1x", lib = "font-awesome"),
      box_height = "120px", 
      selectInput(inputId = "selectData", label = "choose dataset", 
                  choices = c("ABCD", "EFGH"))
    )
  )
)

server <- function(input, output){

  output$barChart <- renderPlot({
    ggplot(data = usedDataFrame, aes(x = company, y = revenue)) +
      geom_bar(width = 1, stat = "identity")
  })

  observeEvent(input$selectData, {
    if (input$selectData == "ABCD"){
      usedDataFrame <- dataFrameA
    }else{
      usedDataFrame <- dataFrameB
    }
  })

}

shinyApp(ui = ui, server = server)

You will see that in spite of the user choosing a different data-set in the "data" tab the graph in the "graph" tab does not change. I would have expected it to change, however, as the observeEvent block below changes the global variable usedDataFrame that the graph is based on. Can anyone give me a hint as to how my assumption above is wrong or point out a smarter way of updating the underlying data-set? Easy explanations would help most (I don't have a technical background).

Upvotes: 0

Views: 477

Answers (2)

Ben
Ben

Reputation: 30474

The output$barChart is a reactive endpoint. This simplest way to modify your code is to have it use the reactive source input$selectData directly. Whenever the input changes, your output will be notified that it needs to re-execute.

That would look like this:

output$barChart <- renderPlot({

    if (input$selectData == "ABCD"){
      usedDataFrame <- dataFrameA
    }else{
      usedDataFrame <- dataFrameB
    }

    ggplot(data = usedDataFrame, aes(x = company, y = revenue)) +
      geom_bar(width = 1, stat = "identity")
})

And you would not need observeEvent at all.

An alternative, you can make usedDataFrame reactive, and call from your renderPlot:

usedDataFrame <- reactive({
  if (input$selectData == "ABCD"){
    dataFrameA
  }else{
    dataFrameB
  }
})

output$barChart <- renderPlot({
  ggplot(data = usedDataFrame(), aes(x = company, y = revenue)) +
    geom_bar(width = 1, stat = "identity")
})

Upvotes: 1

Kevin
Kevin

Reputation: 2044

You were very close to your approach but what is an easier approach than dealing with global variables can be accomplished using reactive which will auto-update based on your input.

library(shiny)
library(shinyWidgets)
library(dplyr)
library(ggplot2)

dataFrameA <- data.frame(
  company = c("A", "B", "C", "D"), 
  revenue = runif(4, min = 0, max = 1000000)
)

dataFrameB <- data.frame(
  company = c("E", "F", "G", "H"), 
  revenue = runif(4, min = 0, max = 1000000)
)

ui <- fluidPage(
  verticalTabsetPanel(
    verticalTabPanel(
      title = "graph", icon = icon("chart-bar", "fa-1x", lib = "font-awesome"),
      box_height = "120px", 
      plotOutput(outputId = "barChart")
    ), 
    verticalTabPanel(
      title = "data", icon = icon("database", "fa-1x", lib = "font-awesome"),
      box_height = "120px", 
      selectInput(inputId = "selectData", label = "choose dataset", 
                  choices = c("ABCD", "EFGH"))
    )
  )
)

server <- function(input, output){

  dat <- reactive({
    if (input$selectData == "ABCD") {
      return(dataFrameA)
    }
    else if (input$selectData == "EFGH") {
      return(dataFrameB)
    }
  })

  output$barChart <- renderPlot({
    ggplot(data = dat(), aes(x = company, y = revenue)) +
      geom_bar(width = 1, stat = "identity")
  })

}

shinyApp(ui = ui, server = server)

Upvotes: 1

Related Questions