Multiple UI Output Based on File Upload Using Shiny Modules - r

Dataset Viewer
Hello, I am attempting to create a shiny application that allows to user to view their uploaded datasets individually.
When there is no file uploaded a message appears asking the user to upload their file...once a csv file is uploaded, the message disappears and shows the users uploaded datasets.
What I've tried
I've tried: using conditionalPanels in app.R & upload.R, creating a separate R file exclusively for each ui condition. I believe my issue is that output$table (function that renders mainpanel ui) is not being triggered after the file uploads.
My issue
Once the user uploads a csv file(any readable csv file), the pre-existing message is not being replaced by the uploaded datasets.
upload.R
data = list()
numDatasets = 0
uploadSideUI <- function(id) {
ns <- NS(id)
tagList(
h2("Dataset Viewer"),
fileInput(ns("file"),label = "Upload File", multiple = FALSE, accept = ".csv")
)
}
uploadMainUI <- function(id) {
ns <- NS(id)
uiOutput(ns("table"))
}
uploadServer <- function(id) {
moduleServer(id, function(input,output,session){
observeEvent(eventExpr = input$file,
handlerExpr = {
df <- read.csv(file = input$file$datapath,header = FALSE)
data <<- c(data,list(df))
numDatasets <<- numDatasets + 1
})
output$table <- renderUI({
if(numDatasets ==0){
h2("please upload file")
}else{
req(input$file)
print(numDatasets)
lapply(1:numDatasets,function(i) {
dataframe = data[[i]]
tagList(
h2(paste("dataset",i)),
hr(),
datatable(dataframe,rownames = FALSE, option = list(scrollY="300px",searching=FALSE)),
br()
)
})
}
})
})
}
app.R
#app.R
library(DT)
library(shiny)
source("testModule.R")
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
uploadSideUI("uploadPage")
),mainPanel(
uploadMainUI("uploadPage")
)
)
)
server <- function(input, output, session) {
uploadServer("uploadPage")
}
shinyApp(ui = ui, server = server)
I am new to the modulization process in shiny, so if you have any other suggestions please point them out! Thanks in advance!

Try this
uploadServer <- function(id) {
moduleServer(id, function(input,output,session){
rv <- reactiveValues(numDatasets = 0)
observeEvent(eventExpr = input$file,
handlerExpr = {
df <- read.csv(file = input$file$datapath,header = FALSE)
data <<- c(data,list(df))
rv$numDatasets <<- rv$numDatasets + 1
})
output$table <- renderUI({
if(rv$numDatasets == 0){
h2("please upload file")
}else{
req(input$file)
print(rv$numDatasets)
lapply(1:rv$numDatasets,function(i) {
dataframe = data[[i]]
tagList(
h2(paste("dataset",i)),
hr(),
datatable(dataframe,rownames = FALSE, option = list(scrollY="300px",searching=FALSE)),
br()
)
})
}
})
})
}

Related

Dynamic UI/Server Modules in Shiny Dashboard Based on Inputs in UI

Let's say I have 4 sets of UI/Server modules in 4 different directories ("./X1/Y1/", "./X1/Y2/", "./X2/Y1/", "./X2/Y2/"). I want to load the selected set based on the input in the sidebar.
I tried using source() within dashboardBody(), but I was not successful.
library(shiny)
library(shinydashboard)
# path to modules
in_path <- "C:/a/b/c/"
# ui
ui <- dashboardPage(
dashboardHeader(title = "test"),
dashboardSidebar(
br(),
selectInput('f1', 'Folder 1', choices = c("X1", "X2")),
helpText(""),
selectInput('f2', 'Folder 2', choices = c("Y1", "Y2")),
br(),
actionButton("load", "Load", icon("thumbs-up"), width = "85%")
),
dashboardBody(
# UI module here from, e.g., "C:/a/b/c/X1/Y2/my_UI.R"
)
)
# server
server <- function(input, output, session) {
# server module here from, e.g., "C:/a/b/c/X1/Y2/my_Server.R"
}
shinyApp(ui, server)
As shiny modules are simply functions, I'd source them in the beginning, and use uiOutput to display the differnt modules.
Here's a working example of the general idea (sample module code proudly stolen from the official Shiny documentation):
<mod1.R>
counterButton <- function(id, label = "Counter") {
ns <- NS(id)
tagList(
actionButton(ns("button"), label = label),
verbatimTextOutput(ns("out"))
)
}
counterServer <- function(id) {
moduleServer(
id,
function(input, output, session) {
count <- reactiveVal(0)
observeEvent(input$button, {
count(count() + 1)
})
output$out <- renderText({
count()
})
count
}
)
}
<mod2.R>
csvFileUI <- function(id, label = "CSV file") {
ns <- NS(id)
tagList(
fileInput(ns("file"), label),
checkboxInput(ns("heading"), "Has heading"),
selectInput(ns("quote"), "Quote", c(
"None" = "",
"Double quote" = "\"",
"Single quote" = "'"
))
)
}
csvFileServer <- function(id, stringsAsFactors = TRUE) {
moduleServer(
id,
## Below is the module function
function(input, output, session) {
# The selected file, if any
userFile <- reactive({
# If no file is selected, don't do anything
validate(need(input$file, message = FALSE))
input$file
})
# The user's data, parsed into a data frame
dataframe <- reactive({
read.csv(userFile()$datapath,
header = input$heading,
quote = input$quote,
stringsAsFactors = stringsAsFactors)
})
# We can run observers in here if we want to
observe({
msg <- sprintf("File %s was uploaded", userFile()$name)
cat(msg, "\n")
})
# Return the reactive that yields the data frame
return(dataframe)
}
)
}
<app.R>
library(shiny)
source("mod1.R")
source("mod2.R")
my_mods <- list("Counter Button" = list(ui = counterButton,
server = counterServer),
"CSV Uploader" = list(ui = csvFileUI ,
server = csvFileServer))
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("mod_sel",
"Which Module should be loaded?",
names(my_mods))
),
mainPanel(
uiOutput("content"),
verbatimTextOutput("out")
)
)
)
server <- function(input, output) {
uuid <- 1
handler <- reactiveVal()
output$content <- renderUI({
my_mods[[req(input$mod_sel)]]$ui(paste0("mod", uuid))
})
observeEvent(input$mod_sel, {
handler(my_mods[[req(input$mod_sel)]]$server(paste0("mod", uuid)))
uuid <<- uuid + 1
})
output$out <- renderPrint(req(handler())())
}
shinyApp(ui, server)
Some Explanation
You put the module code in mod[12].R and it is rather straight forward.
In your main app, you load both(!) source files and for housekeeping reasons, I put both modules functions (ui and server) in a list, but this is not strictly necessary, but facilitates future extension.
In your UI you have an uiOutput which renders dynamically according to the selected module.
In your server you put the code to dynamically render the UI and call the respective server function.
The uid construct is basically there to force a fresh render, whenever you change the selection. Otherwise, you may see still some old values whenever you come back to a module which you have rendered already.

How to store inputed table shiny

I have this shiny app. The main aim is to upload excel sheet with data and plot some graphs in tabs. User is able to select a sheet to make the graph. The seet will render to observe the selected data. This works well.
But I am struggling to manipulate with input data to make the graph.
I tried to use reactive value named data and then make the graph from that. I am quite new with shiny apps.
library(shiny)
library(readxl)
library(dplyr)
library(tidyverse)
library(lubridate)
ui <- fluidPage(
titlePanel("OTD project update"),
sidebarPanel(
fileInput('file1', 'Insert File', accept = c(".xlsx")),
textInput('file1sheet','Name of Sheet (Case-Sensitive)')),
mainPanel(tabsetPanel(
type = "tabs",
tabPanel("Data", tableOutput("value")),
tabPanel("OTD", plotOutput("OTD"))
)
)
)
server <- function(input, output) {
sheets_name <- reactive({
if (!is.null(input$file1)) {
return(excel_sheets(path = input$file1$datapath))
} else {
return(NULL)
}
})
output$value <- renderTable({
if (!is.null(input$file1) &&
(input$file1sheet %in% sheets_name())) {
return(read_excel(input$file1$datapath,
sheet = input$file1sheet))
} else {
return(NULL)
}
})
data <- reactive({
if (!is.null(input$file1) &&
(input$file1sheet %in% sheets_name())) {
return(read_excel(input$datapath,
sheet = input$file1sheet))
} else {
return(NULL)
}
})
}
shinyApp(ui, server)
It may be better to use the sheet names in radio buttons to pick instead of typing it. Also, there was a typo. Try this
library(shiny)
library(readxl)
library(dplyr)
library(tidyverse)
library(lubridate)
library(DT)
ui <- fluidPage(
titlePanel("OTD project update"),
sidebarPanel(
fileInput('file1', 'Insert File', accept = c(".xlsx")),
#textInput('file1sheet','Name of Sheet (Case-Sensitive)')
uiOutput("sheet")
),
mainPanel(tabsetPanel(
type = "tabs",
tabPanel("Data", DTOutput("table")),
tabPanel("OTD", plotOutput("plot"))
)
)
)
server <- function(input, output) {
sheets_name <- reactive({
if (!is.null(input$file1)) {
return(excel_sheets(path = input$file1$datapath))
} else {
return(NULL)
}
})
data <- reactive({
req(sheets_name())
if (!is.null(input$file1)) {
return(read_excel(input$file1$datapath, sheet = input$mysheet))
} else {
return(NULL)
}
})
output$sheet <- renderUI({
req(sheets_name())
radioButtons("mysheet", "Select a Sheet", choices = sheets_name())
})
output$table <- renderDT(data())
output$plot <- renderPlot({plot(cars)})
}
shinyApp(ui, server)

About shiny branch processing using outputUI

We are currently developing Shiny APP.
When Option A or Option B is selected from pickerinput
If you select Option A, an open file dialog opens. Then select the text file. When I select a text file, I want the title of the text file to be displayed.
When I select OptionB, I want to display nothing and do nothing.
I wrote a sample codee.
library(shiny)
library(shinyWidgets)
library(shinyFiles)
ui <- fluidPage(
pickerInput(
inputId = "Pi1",
label = "SELECT!!",
choices = list(c("OptionA"),
c("OptionB")),
options = list(`actions-box` = TRUE,size = 7),
multiple = FALSE,
),
uiOutput("button"),
uiOutput("Message")
)
server <- function(input, output, session) {
OutputUi_func(input,output)
observeEvent(input$file, {
volumes <- c("Documents"=Sys.getenv("HOME"))
shinyFileChoose(input,'file', session=session,roots=volumes, filetypes=c('', 'txt'))
if(length(input$file) <= 1) return({})
fname <- unlist(input$file)
fname <- c(fname[2][1])
ftitle <- "FileName:"
fname <- paste0(ftitle,fname)
output$filename <- renderText({
paste0(fname)
})
})
}
OutputUi_func <- function(input,output){
output$button <- renderUI({
req(input$Pi1)
if(input$Pi1 %in% c("OptionA")){
shinyFilesButton('file', 'Read File', 'select file', FALSE)
}else return(NULL)})
output$Message <- renderText({
req(input$Pi1)
if(input$Pi1 %in% c("OptionA")){
textOutput("filename")
}else return(NULL)})
}
shinyApp(ui, server)
The code has a problem.
・When I execute the code, I get an error about "cat".
・ Even if you select a text file,The file title is not displayed.
What should I do to avoid the above two errors?
Try :
library(shiny)
library(shinyWidgets)
library(shinyFiles)
ui <- fluidPage(
pickerInput(
inputId = "Pi1",
label = "SELECT!!",
choices = list(c("OptionA"),
c("OptionB")),
options = list(`actions-box` = TRUE,size = 7),
multiple = FALSE,
),
uiOutput("button"),
uiOutput("Message")
)
server <- function(input, output, session) {
OutputUi_func(input,output)
observeEvent(input$file, {
if(input$Pi1 %in% "OptionB") return(NULL)
volumes <- c("Documents"=Sys.getenv("HOME"))
shinyFileChoose(input,'file', session=session,roots=volumes, filetypes=c('', 'txt'))
if(length(input$file) <= 1) return(NULL)
output$Message <- renderText({
if(length(input$file) <= 1 || input$Pi1 %in% "OptionB") return(NULL)
sprintf("FileName:%s", unlist(input$file$files)[[3]])
})
})
}
OutputUi_func <- function(input,output){
output$button <- renderUI({
if(input$Pi1 %in% "OptionA")
shinyFilesButton('file', 'Read File', 'select file', FALSE)
else return(NULL)
})
}
shinyApp(ui, server)

Shiny Selectinput for every non-numeric column of a Filteinput data

I would like to create a Shiny dashboard where:
1) The user selects an excel file
2) A Selectinput option is created for every non-numeric column of the obtained dataframe is created that gives users the option to select every unique value in this column. This is how my code looks so far:
library(shiny)
library(dplyr)
library(openxlsx)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput('datafile', 'Choose xlsx file',
accept = c(".xlsx")),
lapply(names("varselect"), function(i) {
selectInput(paste0(i), paste0(i),
choices = "varselect"[i],
multiple = TRUE,
selected = "")})),
mainPanel()))
server <- function(input, output,session) {
Data <- reactive({
infile <- input$datafile
if (is.null(infile)) {
return(data.frame())
}
read.xlsx(infile$datapath,
sheetIndex = 1) %>% select_if(colSums(!is.na(.)) > 0%>% select_if(~!is.numeric(.x)) %>% head())
})
output$varselect <- renderUI({
})
observe({
lapply(names(Data()), function(i) {
selectInput(paste0(i), paste0(i),
choices = Data()[i],
multiple = TRUE,
selected = "")})})
}
shinyApp(ui = ui, server = server)
I am aware of the fact that the code is not working and that there are some grave mistakes, but I hope that somebody can help me to find an approach that does work for this problem. I am rather new to Shinny and that is why i honestly have no clue how to solve this problem.
Many thanks.
In your ui, you can include a uiOutput which will contain all of the dynamically created selectInput widgets.
Then, in output$varselect you can create the selectInput based on the number of columns in Data().
library(shiny)
library(dplyr)
library(openxlsx)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput('datafile', 'Choose xlsx file',
accept = c(".xlsx")),
uiOutput("varselect")
),
mainPanel()
)
)
server <- function(input, output, session) {
Data <- reactive({
infile <- input$datafile
if (is.null(infile)) {
return(data.frame())
}
read.xlsx(infile$datapath, sheet = 1) %>%
select_if(colSums(!is.na(.)) > 0) %>%
select_if(~!is.numeric(.x)) %>%
head()
})
output$varselect <- renderUI({
if (ncol(Data() > 0)) {
lapply(1:ncol(Data()), function(i) {
selectInput(inputId = paste0("si_", i),
label = paste0("Input #", i),
choices = Data()[,i],
multiple = TRUE)
})
}
})
}
shinyApp(ui = ui, server = server)

How to upload multiple tables and display them separately in r shiny?

I want to upload multiple tables and display them separately.
For example: I would like to upload N tables (I don't know N in advance) and want to display them in the main panel as:
Table1:
Table2:
Table3:
.....
My code is shown below but it did not work. How to change it?
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput(
inputId = "calfile",
label = "Choose CSV File",
multiple = TRUE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv")
)
),
mainPanel(
tableOutput("contents")
)
)
)
server <- function(input, output) {
data<-reactive({
if (is.null(input$calfile))
return()
else
{
nfile<-nrow(input$calfile)
csv=list()
for(i in 1: nfile)
{
csv[[i]]=read.csv(input$calfile$datapath[i])
}
}
})
output$contents<- renderTable(data())
}
shinyApp(ui, server)
Many thanks.
Here is an example that may be helpful:
https://stackoverflow.com/a/35943224/3460670
Edit: Try this for your server for creating N tables. You can read in your N data files in a list, and dynamically create outputs for the N tables in an observe expression.
server <- function(input, output) {
observe({
if (!is.null(input$calfile)) {
N_tables = length(input$calfile[, 1])
upload <- list()
for (i in 1:N_tables) {
upload[[i]] <- read.csv(input$calfile$datapath[i])
}
output$contents <- renderUI({
table_output_list <- lapply(1:N_tables, function(i) {
tableOutput(paste0("table_name", i))
})
do.call(tagList, table_output_list)
})
for (i in 1:N_tables) {
local({
my_i <- i
output[[paste0("table_name", my_i)]] <- renderTable({
upload[[my_i]]
})
})
}
}
})
}

Resources