How to use the output of one module in another module

1,205 views
Skip to first unread message

Niket Doshi

unread,
Jan 26, 2017, 8:03:51 PM1/26/17
to Shiny - Web Framework for R

Hi, 

I've been struggling for some time to figure out how can I use the output of one independent module into another without explicitly passing the reactive expression in the call module function.

For ex. I have two modules, module1 returns a filtered data.frame and module2 returns a data table object of a selected data frame from a drop down menu. Ideally, I would like to call module1 multiple times to perform different filtering tasks, store the returned values in reactive expressions. Module2 should automatically get the list of those reactive expressions and create a dropdown menu and eventually render a data table of the selected choice.


So, if I have a shiny dashboard with 2 tabs. The first tab calls module1 3 times and the second tab calls module2 1 time. 

UI
ui <- dashboardPage(
  dashboardHeader(),
  dashboardSidebar(
    sidebarMenu(
      menuItem(
        text = "Filter Tab",
        tabName = "tab1"
      ),
      menuItem(
        text = "Table Tab",
        tabName = "tab2"
      )
    )
  ),
  dashboardBody(
    tabItems(
      tabItem(
        tabName = "tab1",
        absolutePanel(top = 80, right = 530, width = 200, module1UI("filter1")),
        absolutePanel(top = 80, right = 300, width = 200, module1UI("filter2")),
        absolutePanel(top = 80, right = 70, width = 200, module1UI("filter3")),
      ),
      tabItem(
        tabName = "tab2",
        fluidPage(
          fluidRow(
            module2UI("table")
          )
        )
      )
    )
  )
)


Server
server <- function(input, output){
  data1 <- callModule(module1, "filter1")
  data2 <- callModule(module1, "filter2")
  data3 <- callModule(module1, "filter3")
  
  callModule(module2, "table")
}


Module1 
module1UI <- function(id){
  ns <- NS(id)
  tagList(wellPanel(selectInput(ns("filterTask"), "Filters", choices = "<some filter choices>")))
}

module1 <- function(input, output, session, dataSet){
  filterExp <- reactive({
    switch(input$filterTask,
           ...)
  })
  filteredData <- reactive({ get(dataSet) %>% filterExp()})
  
  return(filteredData)
}

Module2
module2UI <- function(id){
  ns <- NS(id)
  
  tagList(fluidRow(
    column(width = 3, selectInput(ns("dataSel"), "Select a dataset", choices = "<some dataset choices>")),
    column(width = 9, dataTableOutput(ns("table")))
  ))
}

module2 <- function(input, output, session){
  # here I would like to get a list of 3 datasets, in this case list(data1, data2, data3),
  # so that I can use that to update the choices for the select input
  # let's get the names of the datasets from the list named dataList
  
  choices <- reactive({ names(dataList())})
  
  observe({
    updateSelectInput(session, "dataSel", choices = choices())
  })
  
  # now using the selected dataset, I would like to get the data of that dataset
  getData <- reactive({dataList()[[input$dataSel]]})
  
  output$table <- renderDataTable({getData()})
}

I know it can be done by explicitly passing "data1", "data2" and "data3" as arguments to module2, but that would be hardcoded and I would like to avoid that.

Next is to save the session environment as a variable, like
env <- environment() 
in server before calling module2 and then pass the environment variable to the module and then saving a list of variables in the server logic of the module
varList <- ls(env)

However, this will not only get the desired datasets, but also all the other variables that are in that environment and another problem with this is I've no clue how to get the actual data from using the variable name. 

Thanks a lot in advance for the help! 

Best,
Niket

Joe Cheng

unread,
Jan 26, 2017, 10:12:31 PM1/26/17
to Niket Doshi, Shiny - Web Framework for R
> I know it can be done by explicitly passing "data1", "data2" and "data3" as arguments to module2, but that would be hardcoded and I would like to avoid that.

Can you elaborate a bit on why hardcoding here is something to be avoided? There are reasons I would agree with but it's unclear to me which ones apply here.
--
You received this message because you are subscribed to the Google Groups "Shiny - Web Framework for R" group.
To unsubscribe from this group and stop receiving emails from it, send an email to shiny-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/shiny-discuss/6e95c6e1-0626-479c-a448-d02e7a38277b%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Niket Doshi

unread,
Jan 27, 2017, 8:22:21 AM1/27/17
to Shiny - Web Framework for R, niket...@thinkbiganalytics.com
Hi Joe, 

Thanks for your reply. 

There are a couple of reasons why I would like to avoid that. 
1. Since this is just an example, I've only called module1 thrice, however it can get really messy and I can't presume how many times a user might call module1.
2. Moreover, as the module2 will be used as a function, I don't want to hard code on how many reactive expressions it should accept as its arguments.
3. Finally, I could ask the user to pass a list of the reactive expressions ("data1", "data2", "data3"...) to the module2, but that would be asking too much :)

I hope these are some of the reasons you were thinking about. I would be happy to learn some other reasons as well where this would make sense. 

Best,
Niket

Joe Cheng

unread,
Jan 30, 2017, 3:07:13 AM1/30/17
to Niket Doshi, Shiny - Web Framework for R
I'd do something like this then:

  datasets <- list()
  datasets$data1 <- callModule(module1, "filter1")
  datasets$data2 <- callModule(module1, "filter2")
  datasets$data3 <- callModule(module1, "filter3")
  
  callModule(module2, "table", datasets)

The important thing here is that a human be able to inspect the code at any individual scope--in this case, the shiny app's server function--and understand the relationship between the modules. If the "table" module just "automagically" knew about data1, data2, and data3 variables without any explicit relationship between them from the caller's perspective, that makes it hard to reason about these relationships. As I've written it here, it's very clear how data passes from filters 1-3 to table.

Does that make sense to you?

Niket Doshi

unread,
Jan 30, 2017, 11:09:01 AM1/30/17
to Shiny - Web Framework for R, niket...@thinkbiganalytics.com
I wanted the "table" module to function exactly as you mentioned - "automagically" know about the data. 

But, now it makes sense why I shouldn't do it. 

Thanks!

Best,
Niket
Reply all
Reply to author
Forward
0 new messages