Shiny stopped working after modularizing the app

Hi Shiny Experts,

Please find the below working unmodularized and modularized non-working code below.
Could you please guide us on which part we made mistake ? Because when tried Browser() to debug, it stopped running itself

library(shiny)
library(readxl)

# Increase band width for shiny to handle bigger file
options(shiny.maxRequestSize = 30 * 1024 ^ 2)

# Function to read excels 
read_excel_allsheets <- function(filename, tibble = FALSE) {
  sheets <- readxl::excel_sheets(filename)
  x <-
    lapply(sheets, function(X)
      readxl::read_excel(
        filename,
        sheet = X,
        col_names = T,
        skip = 5
        ,
        col_types = "text"
      ))
  if (!tibble)
    x <- lapply(x, as.data.frame)
  names(x) <- sheets
  x
}

ui <- fluidPage(fluidPage(
  titlePanel("Uploading Files"),
  sidebarLayout(
    sidebarPanel(
      fileInput(
        'file1',
        'Choose XLSX File (Convert xls to xlsx)',
        accept = c('.xlsx')
      ),
      tags$hr(),
      downloadButton('downloadData', 'Download')
    ),
    mainPanel(DT::dataTableOutput("contents"))
  )
))

server <- function(input, output) {
  getData <- reactive({
    inFile <- input$file1
    if (is.null(input$file1))
      return(NULL)
    mysheets <- read_excel_allsheets(inFile$datapath)
# SheetName is Table
    data <- mysheets$Table
    data
  })
  output$contents <- DT::renderDataTable({
    DT::datatable(getData(), options = list(pageLength = 7, scrollX = TRUE))
  })
  
  output$downloadData <- downloadHandler(
    filename = function() {
      paste("Pricing Dimension for PPM dated-", Sys.Date(), ".xlsx")
    },
    
    content = function(file) {
      write_xlsx(getData(), file)
    }
  )
}
shinyApp(ui, server)

Modularized code

library(shiny)
library(readxl)

# Increase band width for shiny to handle bigger file 
options(shiny.maxRequestSize=30*1024^2) 

# Function to read all excel sheet necessary
read_excel_allsheets <- function(filename, tibble = FALSE) {
  sheets <- readxl::excel_sheets(filename)
  x <- lapply(sheets, function(X) readxl::read_excel(filename, sheet = X, col_names = T, skip = 5
                                                     ,col_types = "text"
  ))
  if(!tibble) x <- lapply(x, as.data.frame)
  names(x) <- sheets
  x
}

# Module UI to display sidebar content
mod_example_readDownloadUI <- function(id) {
  ns <- shiny::NS(id)
  shiny::tagList(
    fileInput(ns("file1"), "Choose XLSX File (Convert xls to xlsx)",accept=c(".xlsx")),
    tags$hr(),
    downloadButton(ns("downloadData"), "Download")
  )
}

# Module UI to display Body content
mod_example_displayUI <- function(id) {
  ns <- shiny::NS(id)
  shiny::tagList(
    DT::dataTableOutput(ns("contents"))
  )
}

# Server function to display
mod_example_display <- function(input, output, session) {
  getData <- reactive({
    inFile <- input$file1
    if (is.null(input$file1))
      return(NULL)
    mysheets <- read_excel_allsheets(inFile$datapath)
    data <- mysheets$Table
    data
  })
  
  output$contents <- DT::renderDataTable({
    DT::datatable(getData()
                  ,options = list(pageLength = 7,scrollX = TRUE))
  })
}

# Server function to download the displayed data
mod_example_download <- function(input, output, session){
  getData <- reactive({
    inFile <- input$file1
    if (is.null(input$file1))
      return(NULL)
    mysheets <- read_excel_allsheets(inFile$datapath)
# Sheet Name was Table
    data <- mysheets$Table
    data
  })
  
  output$downloadData <- downloadHandler(
    filename = function() {
      paste("updated file dated-", Sys.Date(), ".xlsx")
    },
    content = function(file) {
      write_xlsx(DT::datatable(getData(),file))
    }
  )
}

ui <- fluidPage(
  shinydashboard::dashboardPage(
    skin = "yellow",
    # HEADER -----
    shinydashboard::dashboardHeader(
      title = "Modularizing App"
    ),
    # SIDEBAR -----
    shinydashboard::dashboardSidebar(
      shinydashboard::sidebarMenu(
        shinydashboard::menuItem('Example', tabName = 'example', icon = shiny::icon('file')),
        shinydashboard::tabItems(
          shinydashboard::tabItem("example", mod_example_readDownloadUI("example_sidemod"))
        )
      ) 
    ),
    # BODY -----
    shinydashboard::dashboardBody(
      shinydashboard::tabItems(
        shinydashboard::tabItem("example", mod_example_displayUI("example_bodymod"))
      )
    )
  )
)
server <- function(input, output) {
  shiny::callModule(mod_example_display, "example_sidemod")
  shiny::callModule(mod_example_download, "example_bodymod")
}
shinyApp(ui,server)

Hi,

I wish I can help you, however, the first version of your code returns an error:

ERROR: Argument x must be a data frame or list of data frames

What I can see from the code: modules are not well organized (namespace issue, session argument missing from the server function, ...), which is pretty confusing. I would create 3 modules:

  • 1 for uploading data and returning the reactive expression containing these data, which will prevent your from having getData() in 2 different modules with different namespaces
  • 1 for displaying data
  • 1 for downloading data
1 Like

Thanks for response and feedback.

server has no session because callModules have session within itself.
Also, currently, there are 2 UI and 2 Modules
each one for displaying and downloading.

Thanks for suggestion.
Please find the solution below.

Although it is no elegant but much appreciated if UI could be improved/suggested

library(shiny)
library(magrittr) # Load magrittr for the piping operator %>%
library(DT)
library(readxl)
library(tidyselect)
library(writexl)
library(dplyr)
library(tidyr)
library(readxl)
library(stringr)

# Increase band width for shiny to handle bigger file 
options(shiny.maxRequestSize=30*1024^2) 

# Function to read all excel sheet necessary
read_excel_allsheets <- function(filename, tibble = FALSE) {
  sheets <- readxl::excel_sheets(filename)
  x <- lapply(sheets, function(X) readxl::read_excel(filename, sheet = X, col_names = T, skip = 5
                                                     ,col_types = "text"
  ))
  if(!tibble) x <- lapply(x, as.data.frame)
  names(x) <- sheets
  x
}

# Module UI to read content
mod_readUI <- function(id) {
  ns <- shiny::NS(id)
  shiny::tagList(
    fileInput(ns("file1"), h6("Choose xlsx file")
              ,accept=c(".xlsx"))
  )
}

# Module UI to display content
mod_displayUI <- function(id) {
  ns <- shiny::NS(id)
  shiny::tagList(
    DT::dataTableOutput(ns("contents"))
  )
}

# Module UI to download content
mod_downloadUI <- function(id) {
  ns <- shiny::NS(id)
  shiny::tagList(
    downloadButton(ns("downloadData"), "Download")
  )
}

# Server functions
mod_display <- function(input, output, session, file) {
  # In case want to alter the data to download
  # myfile = reactive({as.data.frame(file()[,1])})
  output$contents <- DT::renderDataTable({
    DT::datatable(file()
                  ,options = list(pageLength = 7,scrollX = TRUE))
  })

  reactive({
    file()
    # myfile()
  })
}

mod_read <- function(input, output, session){
  
  getData <- reactive({
    req(input$file1)
    inFile <- input$file1
    mysheets <- read_excel_allsheets(inFile$datapath)
    ppm <- mysheets$Download
    ppm
  })
  
  ### In ordert to send data as reactive 
  reactive({
    getData()
  })
}

mod_download <- function(input, output, session, displayData){
  
  output$downloadData <- downloadHandler(
    # browser(),
    filename = function() {
      paste("Updated file dated-", Sys.Date(), ".xlsx")
    },
    
    content = function(file) {
      write_xlsx(displayData(),file)
    }
  )
  
}

ui <- fluidPage(
  
  shinydashboard::dashboardPage(
    skin = "yellow",
    # HEADER -----
    shinydashboard::dashboardHeader(
      title = "Modularizing App"
    ),
    # SIDEBAR -----
    shinydashboard::dashboardSidebar(
      shinydashboard::sidebarMenu(id = "menu",
        shinydashboard::menuItem('Example', tabName = 'example', icon = shiny::icon('file')),
        
        conditionalPanel("input.menu == 'example'",
                         shinydashboard::menuSubItem(mod_readUI("sidemod")),
                         shinydashboard::menuSubItem(mod_downloadUI("downmod"))
        )
      ) 
    ),
    # BODY -----
    shinydashboard::dashboardBody(
      shinydashboard::tabItems(
        shinydashboard::tabItem("example", mod_displayUI("bodymod"))
      )
    )
  )
)

server <- function(input, output) {
  readFile <- shiny::callModule(mod_read, "sidemod")
  displayFile <- shiny::callModule(mod_display, "bodymod", file = readFile)
  shiny::callModule(mod_download, "downmod", displayFile)
  
}

shinyApp(ui,server)

Cool! One last remark: you don’t need to embed the dashboardPage in a fluidPage.

1 Like

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