Generating a Data Frame as Reactive Expression from API Data in Shiny Application (updated)

Hi all,

I have been working on a shiny application, and have hit a real stumbling block, and am hoping to get some feedback. I am using shiny to generate a dashboard to display an individuals results from an online survey. In order to have the results for an individual displayed I am using session data (session$clientData$url_search).

because this data is "reactive" the only way i have been able to successfully implement the system is to call it within the renderPlot() functions for each graph i am displaying. This results in me connecting to the API and rebuilding the same data frame for each plot I want to create. I am seeking out a way to establish the connection and build the data frame once, and then use that within each of my calls to build the plots.

Here is how the application currently generates a plot:

server <- function(input, output, session) {
  
  #create the URL parameters 
  url_parameters <- reactiveValues()
  
  observe({
    query <- parseQueryString(session$clientData$url_search)
    url_parameters$subject_id <- query[["id"]]
  })

  #establish connection with the API to access the database
  project <- redcapConnection(url, token)
  
  
  #Generate the oral summary graph
  output$graph_1 <- renderPlot({
    graph_data <- exportRecords(project,records = url_parameters$subject_id) %>%
      #select only the fields being assessed
      select(col_1, col_2)
    #transmute the table to make the data easier to plot
    data <-  transmute(graph_data,
                       x_axis = col_1 + col_2, 
                       y_axis = col_1 - col_2)
      #this is the R code used to generate the Oral graph
      graph_1 <- ggplot(data, aes(x = x_axis, y = y_axis)) 
      #return statement
      graph_1
    })

I have tried multiple times to take the exportRecords() function, and assignment of that info the the data frame "data" into their own reactive expression, and then use them in the output$graph_1 call, but nothing has worked.

The way I hope it could work:

server <- function(input, output, session) {
  
  #create the URL parameters 
  url_parameters <- reactiveValues()
  
  observe({
    query <- parseQueryString(session$clientData$url_search)
    url_parameters$subject_id <- query[["id"]]
  })

  #establish connection with the API to access the database
  project <- redcapConnection(url, token)
  
  #Generate the data frame 1 time for use in all plots 
  graph_data <- exportRecords(project,records = url_parameters$subject_id) %>%
      #select only the fields being assessed
      select(col_1, col_2)
    #transmute the table to make the data easier to plot
    data <-  transmute(graph_data,
                       x_axis = col_1 + col_2, 
                       y_axis = col_1 - col_2)

  #Generate the oral summary graph
  output$graph_1 <- renderPlot({
      #this is the R code used to generate the Oral graph
      graph_1 <- ggplot(data, aes(x = x_axis, y = y_axis)) 
      #return statement
      graph_1
    })

What kind of function call should surround the creation of graph_data that will not result in the following warning, and still allow me to use the data frame in my renderPlot() call?

Warning: Error in .getReactiveEnvironment()$currentContext: Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)

Thank you in advanced for any suggestions, and please let me know if there is a way I can refactor this to be more informative. I am unsure how to build this into a repex, but I tried to modify the code to be easier to understand.

surely you want the output$graph_1 renderPlot to return oral_graph and not graph_1 that is undefined

You have caught a typo that I made as I rewrote the code. I tried my best to eliminate all the aesthetics and details that were not required for the problem I am hoping to solve. I will edit to post accordingly. Thanks

I'm afraid this is not a reprex, as it cant be run on anyones machine but yours...
as such it will be very challenging to debug.
could you make a reprex ?
Try to make it as simple as possible, with the least UI as possible, and with hardcoding as many things as possible while keeping enough dynamics to ensure that the heart of your issue is expressed.

Please excuse the trouble. i believe that this repex demonstrates my problem.

Thank your for your help and patience

library(shiny)
library(tidyverse)
#define the pretend data frame 
example_frame <- data.frame(col_1 = c(1,1,1,1,1,1,1,1,1,1,1,1,1), 
                           col_2 = c(2,2,2,2,2,2,2,2,2,2,2,2,2), 
                           col_3 = c(3,3,3,3,3,3,3,3,3,3,3,3,3))
ui <- fluidPage(
  titlePanel("test_1"),
  mainPanel(
    plotOutput("graph_1")
  )
)

server <- function(input, output, session) {
  
  url_parameters <- reactiveValues()
  
  observe({
    query <- parseQueryString(session$clientData$url_search)
    url_parameters$subject_id <- query[["id"]]
  })
  
  data <- select(example_frame, col_1, url_parameters$subject_id)
  
  output$graph_1 <- renderPlot({
    #this is the R code used to generate the Oral graph
    graph_1 <- ggplot(data, aes(x = col_1, y = url_parameters$subject_id)) 
    #return statement
    graph_1
  })
}

shinyApp(ui = ui, server = server)

as a minimum fix so that the code runs without erroring out you need

 mydata <-reactive({ select(example_frame, col_1, url_parameters$subject_id)})
  
  output$graph_1 <- renderPlot({

    #this is the R code used to generate the Oral graph
    graph_1 <- ggplot(mydata(), aes(x = col_1, y = url_parameters$subject_id)) 
    #return statement
    graph_1
  })

i renamed data to mydata as data() is a common way to access R internal datasets. more importantly its wrapped as a reactive now..
However, this plots an empty graph. not sure what your intent with the url_parameters, as you dont provvide a way to populate it in this reprex so its null.

Thanks so much. This is exactly what I wanted! Thank you so much!

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.