Search code examples
rshinyshinyappsshinymodules

How to create communication between Shiny modules?


I created an app in shiny containing whose communication between modules does not work properly. A concise description of my app: My application has two selectInputs. It updates the second selectInput depending on the first selectInput and then it plots a plot and a table for df data. I want my app to have three modules: Dataselect module,the Table module, andthe Plot module. I created these modules, but it seems that different modules don'tcommunicat with each other. The selectInputs work well but plot and table aren't built. I have created a minimal example of that. I really appreciate any help everybody can provide.

library(shiny)
library(plotly)
library(reshape2)



#----------------------------------------------------------------------------------------
# Dataselect module
dataselect_ui<- function(id) {
  ns<-NS(id)
  tagList(
    selectInput(ns("Nametype"),"Select a name type",
                choices=c("Name1","Name2","choose"),selected = "choose"),
    
    selectInput(ns("Name"),"Select a name",
                choices="",selected = "",selectize=TRUE)
  )
}
dataselect_server <- function(id) {
  moduleServer(id, function(input, output, session) {
    # Data preparation
    df<-data.frame(Name1<-c("Aix galericulata","Grus grus","    Alces alces"),
                   Name2<-c("Mandarin Duck","Common Crane"  ,"Elk"),
                   eventDate<-c("2015-03-11","2015-03-10","2015-03-10"),
                   individualCount<-c(1, 10, 1)
    )
    colnames(df)<-c("Name1","Name2","eventDate","individualCount")

    # Putting columns Name1 and Nam2 of df in one column called nameType using melt()function
    # This format of data is needed for the choices argument of updateSelectizeInput()
    df2<-reshape2::melt(df,id=c("eventDate","individualCount"))
    colnames(df2)<-c("eventDate","individualCount","nameType","Name")
    
    observeEvent(
      input$Nametype,
      updateSelectizeInput(session, "Name", "Select a name", 
                           choices = unique(df2$Name[df2$nameType==input$Nametype]),selected = ""))
    
    
    # finalDf() is the data used to plot the table and plot
    finalDf<-reactive({
      if(input$Name=="choose"){
        return(NULL)
        
      }
      if(input$Name==""){
        return(NULL)
        
      }
      if(input$Nametype=="choose"){
        return(NULL)
        
      } 
      
      # if the first selectInput is set to Name1, from df select rows their Name1 column are 
      # equal to the second selectInput value
      else if(input$Nametype=="Name1"){
        finalDf<-df[which(df$Name1==input$Name) ,]
        
      } 
      # if the first selectInput is set to Name2, from df select rows their Name2 column are 
      # equal to the second selectInput value
      else if(input$Nametype=="Name2"){
        finalDf<-df[which(df$Name2==input$Name) ,]
        
      }
      return(finalDf)
    })
    
    return(
      list("finalDf" = finalDf, "input_Name" = reactive(input$Name))
    )
  })
}

#-------------------------------------------------------------------------------------
# Table module
table_ui <- function(id) {
  ns<-NS(id)
  tagList(
    DT::DTOutput(ns("tab"))
  )
}

table_server <- function(id) {
  moduleServer(id, function(input, output, session) {
    
    module_outputs <- dataselect_server("dataselect")
    input_Name <- module_outputs$input_Name
    finalDf    <- module_outputs$finalDf
    
    
    output$tab<-DT::renderDT({
      req(input_Name())
      datatable(finalDf(), filter = 'top', 
                options = list(pageLength = 5, autoWidth = TRUE),
                rownames= FALSE)
    })
  })
}
#--------------------------------------------------------------------------------------
# Plot module
plot_ui <- function(id) {
  ns<-NS(id)
  tagList(
    plotlyOutput(ns("plot"))
  )
}

plot_server <- function(id) {
  moduleServer(id, function(input, output, session) {
    
    module_outputs <- dataselect_server("dataselect")
    input_Name <- module_outputs$input_Name
    finalDf    <- module_outputs$finalDf
    
    output$plot <- renderPlotly({
      req(input_Name())
      p<-ggplot(finalDf(),aes(x=eventDate,y=individualCount)) +geom_point(alpha=0.2, shape=21, color="black",fill="red",size=5)+
        labs( x = "Date Event",y= "Individual Count") +theme_bw()
      p<-ggplotly(p)
      p
    })
  })
}
#--------------------------------------------------------------------------------------
# application
ui <- fluidPage(
               dataselect_ui("dataselect"),
               table_ui("table1"),
               plot_ui("plot1")
    )


server <- function(session,input, output) {
  
  dataselect_server("dataselect")
  table_server("table1")
  plot_server("plot1")

}

shinyApp(ui = ui, server = server)

Solution

  • I don't know what was wrong in your code. I changed the rationale of the app: instead of calling the dataselect module in the two other modules, I call it only in the main server and I pass its outputs as arguments of the two other modules.

    The plot appears but not sure the app does what you expect, please tell me.

    library(shiny)
    library(plotly)
    library(reshape2)
    library(DT)
    
    
    #----------------------------------------------------------------------------------------
    # Dataselect module ####
    dataselect_ui <- function(id) {
      ns <- NS(id)
      tagList(
        selectInput(ns("Nametype"), "Select a name type",
          choices = c("Name1", "Name2", "choose"), selected = "choose"
        ),
        selectInput(ns("Name"), "Select a name",
          choices = "", selected = "", selectize = TRUE
        )
      )
    }
    
    dataselect_server <- function(id) {
      moduleServer(id, function(input, output, session) {
        # Data preparation
        df <- data.frame(
          Name1           = c("Aix galericulata", "Grus grus", "    Alces alces"),
          Name2           = c("Mandarin Duck", "Common Crane", "Elk"),
          eventDate       = c("2015-03-11", "2015-03-10", "2015-03-10"),
          individualCount = c(1, 10, 1)
        )
        colnames(df) <- c("Name1", "Name2", "eventDate", "individualCount")
    
        # Putting columns Name1 and Nam2 of df in one column called nameType using melt()function
        # This format of data is needed for the choices argument of updateSelectizeInput()
        df2 <- reshape2::melt(df, id = c("eventDate", "individualCount"))
        colnames(df2) <- c("eventDate", "individualCount", "nameType", "Name")
    
        observeEvent(
          input$Nametype,
          updateSelectizeInput(session, "Name", "Select a name",
            choices = unique(df2$Name[df2$nameType == input$Nametype]), selected = ""
          )
        )
    
        # finalDf() is the data used to plot the table and plot
        finalDf <- reactive({
          if (input$Name == "choose") {
            return(NULL)
          }
          if (input$Name == "") {
            return(NULL)
          }
          if (input$Nametype == "choose") {
            return(NULL)
          }
          # if the first selectInput is set to Name1, from df select rows their Name1 column are
          # equal to the second selectInput value
          if (input$Nametype == "Name1") {
            finalDf <- df[which(df$Name1 == input$Name), ]
          }
          # if the first selectInput is set to Name2, from df select rows their Name2 column are
          # equal to the second selectInput value
          else if (input$Nametype == "Name2") {
            finalDf <- df[which(df$Name2 == input$Name), ]
          }
          return(finalDf)
        })
    
        return(
          list("finalDf" = finalDf, "input_Name" = reactive(input$Name))
        )
      })
    }
    
    #-------------------------------------------------------------------------------------
    # Table module ####
    table_ui <- function(id) {
      ns <- NS(id)
      tagList(
        DTOutput(ns("tab"))
      )
    }
    
    table_server <- function(id, input_Name, finalDf) {
      moduleServer(id, function(input, output, session) {
    
        output$tab <- renderDT({
          req(input_Name())
          datatable(finalDf(),
            filter = "top",
            options = list(pageLength = 5, autoWidth = TRUE),
            rownames = FALSE
          )
        })
        
      })
    }
    
    #--------------------------------------------------------------------------------------
    # Plot module ####
    plot_ui <- function(id) {
      ns <- NS(id)
      tagList(
        plotlyOutput(ns("plot"))
      )
    }
    
    plot_server <- function(id, input_Name, finalDf) {
      moduleServer(id, function(input, output, session) {
    
        output$plot <- renderPlotly({
          req(input_Name())
          p <- ggplot(finalDf(), aes(x = eventDate, y = individualCount)) +
            geom_point(alpha = 0.2, shape = 21, color = "black", fill = "red", size = 5) +
            labs(x = "Date Event", y = "Individual Count") +
            theme_bw()
          p <- ggplotly(p)
          p
        })
      })
      
    }
    
    #--------------------------------------------------------------------------------------
    # application ####
    ui <- fluidPage(
      dataselect_ui("dataselect"),
      table_ui("table1"),
      plot_ui("plot1")
    )
    
    server <- function(session, input, output) {
      x <- dataselect_server("dataselect")
      input_Name <- x$input_Name
      finalDf    <- x$finalDf
      table_server("table1", input_Name, finalDf)
      plot_server("plot1", input_Name, finalDf)
    }
    
    shinyApp(ui = ui, server = server)