10

I'm looking to port some older Shiny apps to use Shiny Modules, but running into trouble trying to port over my reactive expressions.

According to the documentation:

The goal is not to prevent modules from interacting with their containing apps, but rather, to make these interactions explicit. If a module needs to use a reactive expression, take the reactive expression as a function parameter.

I have existing reactive expressions that import data from APIs etc. that I would like to pass in, but can't seem to find the syntax. If I modify the given Shiny module example below I can get to the same problem.

Could anyone modify the below so that you can pass in the car_data() reactive data into the module? I've tried just about every combination of isolate and car_data/car_data() I can think of and am stumped :)

I would prefer to not need to call the data within the module itself, as in my case I'm trying to generalise an ETL function applicable to lots of datasets.

library(shiny)
library(ggplot2)

linkedScatterUI <- function(id) {
  ns <- NS(id)

  fluidRow(
    column(6, plotOutput(ns("plot1"), brush = ns("brush"))),
    column(6, plotOutput(ns("plot2"), brush = ns("brush")))
  )
}

linkedScatter <- function(input, output, session, data, left, right) {
  # Yields the data frame with an additional column "selected_"
  # that indicates whether that observation is brushed
  dataWithSelection <- reactive({
    brushedPoints(data(), input$brush, allRows = TRUE)
  })

  output$plot1 <- renderPlot({
    scatterPlot(dataWithSelection(), left())
  })

  output$plot2 <- renderPlot({
    scatterPlot(dataWithSelection(), right())
  })

  return(dataWithSelection)
}

scatterPlot <- function(data, cols) {
  ggplot(data, aes_string(x = cols[1], y = cols[2])) +
    geom_point(aes(color = selected_)) +
    scale_color_manual(values = c("black", "#66D65C"), guide = FALSE)
}

ui <- fixedPage(
  h2("Module example"),
  linkedScatterUI("scatters"),
  textOutput("summary")
)

server <- function(input, output, session) {

  ### My modification 
  ### making the reactive outside of module call
  car_data <- reactive({
    mpg
    })

  ## This doesn't work
  ## What is the syntax for being able to call car_data()?
  df <- callModule(linkedScatter, "scatters", car_data(),
                   left = reactive(c("cty", "hwy")),
                   right = reactive(c("drv", "hwy"))
  )

  output$summary <- renderText({
    sprintf("%d observation(s) selected", nrow(dplyr::filter(df(), selected_)))
  })
}

shinyApp(ui, server)
MarkeD
  • 2,500
  • 2
  • 21
  • 35
  • This might help: http://stackoverflow.com/a/36517069/4222792 Why do you have to make reactive outside module call? Can't it be output of some of your modules? Your dataImportApi could be one module? – Mikael Jumppanen Apr 18 '16 at 15:08
  • Thanks Mikael. The reason its outside as its for a general module for use in a package, and I'd like to pass any reactive dataframe into it, not just one particular API call. – MarkeD Apr 18 '16 at 19:43

2 Answers2

7

Drop the parens after car_data:

df <- callModule(linkedScatter, "scatters", car_data,
                   left = reactive(c("cty", "hwy")),
                   right = reactive(c("drv", "hwy"))
  )

The module seems to want "unresolved" reactives. The parentheses "resolves" them.

Community
  • 1
  • 1
6

If you want to pass input which is not part of the module just wrap it around reactive() as stated in a tutorial.

If a module needs to access an input that isn’t part of the module, the containing app should pass the input value wrapped in a reactive expression (i.e. reactive(...)): callModule(myModule, "myModule1", reactive(input$checkbox1))

Update: As correctly stated in another answer and Joe Cheng correct way to pass reactive expression is without brackets ()

callModule(linkedScatter, "scatters", car_data)

One option is also to modularize your API input function so you don't need to define reactive expression outside modules. Example of modularized input can be found from this answer. Below your code with right answer.

library(shiny)
library(ggplot2)

linkedScatterUI <- function(id) {
  ns <- NS(id)

  fluidRow(
    column(6, plotOutput(ns("plot1"), brush = ns("brush"))),
    column(6, plotOutput(ns("plot2"), brush = ns("brush")))
  )
}

linkedScatter <- function(input, output, session, data, left, right) {
  # Yields the data frame with an additional column "selected_"
  # that indicates whether that observation is brushed
  dataWithSelection <- reactive({
    brushedPoints(data(), input$brush, allRows = TRUE)
  })

  output$plot1 <- renderPlot({
    scatterPlot(dataWithSelection(), left())
  })

  output$plot2 <- renderPlot({
    scatterPlot(dataWithSelection(), right())
  })

  return(dataWithSelection)
}

scatterPlot <- function(data, cols) {
  ggplot(data, aes_string(x = cols[1], y = cols[2])) +
    geom_point(aes(color = selected_)) +
    scale_color_manual(values = c("black", "#66D65C"), guide = FALSE)
}

ui <- fixedPage(
  h2("Module example"),
  linkedScatterUI("scatters"),
  textOutput("summary")
)

server <- function(input, output, session) {
data(mpg)
  ### My modification 
  ### making the reactive outside of module call
  car_data <- reactive({
    mpg
    })

  ## Fix This doesn't work by reactive (var) no brackets()
  ## What is the syntax for being able to call car_data()?
  df <- callModule(linkedScatter, "scatters", reactive(car_data),
                   left = reactive(c("cty", "hwy")),
                   right = reactive(c("drv", "hwy"))
  )

  output$summary <- renderText({
    sprintf("%d observation(s) selected", nrow(dplyr::filter(df(), selected_)))
  })
}

shinyApp(ui, server)
micstr
  • 5,080
  • 8
  • 48
  • 76
Mikael Jumppanen
  • 2,436
  • 1
  • 17
  • 29