0

I'm trying to make a Shiny application that dynamically makes a number of tabs from a list, all of which contain a fileInput box. Once files (in this case images), I want to show them as they are uploaded on the corresponding tab. I figured to use a reactive function for this (taken from Dynamically display images from upload in Shiny UI) but unfortunately this isn't working for me.

I make use of Shiny modules in this case, and I made one module for the UI (sectionTabUI) and one for the server functions (sectionTabServer). I can output the list of files as a datatable perfectly with output$files <- renderTable(input$files), but trying to make a reactive source from the input fails on me (cannot coerce type 'closure' to vector of type 'character'). I guess it has something to do with namespacing but I can't figure out what it is.

Just to sum up, these are my main goals:

  1. Arbitrary number of unique tabs, each with their own file input
  2. Present uploaded images dynamically per tab

Below is a minimal working example, thanks in advance!

app.R

library(shiny)

sectionTabUI <- function(id, title) {
    ns <- NS(id)
    tabPanel(
        title = title,
        sidebarLayout(
            sidebarPanel(
                tagList(
                    fileInput(
                        inputId = ns("files"),
                        label = paste(id),
                        multiple = TRUE
                    )
                )
            ),
            mainPanel(
                sectionTabServer(id),
                tableOutput(ns("files")),
                # uiOutput("images")
            )
        )
    )
}

sectionTabServer <- function(id) {
    moduleServer(
        id,
        function(input, output, session) {
            ns <- session$ns
            output$files <- renderTable(input$files)


            # TODO: This doesn't work
            # TODO: cannot coerce type 'closure' to vector of type 'character'
            files <- reactive({
                validate(need(input$files))
                # files$datapath <- gsub("\\\\", "/", files$datapath)
                input$files
            })

            # TODO: Commented out as the above isn't working yet
            # output$images <- renderUI({
            #     if (is.null(input$files)) {
            #         return(NULL)
            #     }

            #     image_output_list <- lapply(
            #         1:nrow(files()),
            #         function(i) {
            #             print(i)
            #             imagename <- paste0("image", i)
            #             imageOutput(imagename)
            #         }
            #     )

            #     do.call(tagList, image_output_list)
            # })

            # observe({
            #     if (is.null(input$files)) {
            #         return(NULL)
            #     }
            #     for (i in 1:nrow(files())) {
            #         print(i)
            #         local({
            #             my_i <- i
            #             imagename <- paste0("image", my_i)
            #             print(imagename)
            #             output[[imagename]] <-
            #                 renderImage(
            #                     {
            #                         list(
            #                             src = files()$datapath[my_i],
            #                             alt = "Image failed to render"
            #                         )
            #                     },
            #                     deleteFile = FALSE
            #                 )
            #         })
            #     }
            # })
        }
    )
}

ui <- navbarPage(
    id = "appnavbar",
    "Report",
    tabPanel("settings", actionButton("savestructure", "Save Report Structure")),
    tabPanel(
        "output",
        fluidPage(
            mainPanel(
                tabsetPanel(
                    id = "section_tabs",
                    tabPanel(
                        "Report Settings",
                        textInput("title", "Title", "Title"),
                        dateInput("date", "Date", value = NULL, format = "d MM, yyyy")
                    )
                )
            )
        )
    )
)

# This is just arbitrary at this point, the real application has a different implementation that lets the user control which tabs will be shown
sections <- c("Section 1", "Section 2", "Section 3")

server <- function(input, output, session) {
    # Dynamically adds tabs based on document structure
    observeEvent(input$savestructure, {
        # Remove earlier added tabs if user changes structure
        if (!exists("sections_old")) {
            sections_old <<- c()
        } else if (length(sections_old > 0)) {
            for (section in sections_old) {
                removeTab("section_tabs", section)
            }
        }
        updateTabsetPanel(session, "appnavbar", selected = "output")
        for (section in sections) {
            appendTab(
                inputId = "section_tabs",
                sectionTabUI(id = section, title = section),
            )
        }
        sections_old <<- sections
    })
}

shinyApp(ui = ui, server = server)
plinders
  • 23
  • 4

0 Answers0