I'm developing a rather large shiny app and used the methods from this talk on shinydevcon to modularize it.
Now I was trying to use the package shinyFiles to get a "Save as" type dialog:
library(shiny)
library(shinyFiles)
# Dummy module for MCVE
saveModule <- function(input, output, session) {
# Taken straight from shinyFilesExample()
volumes <- getVolumes()
shinyFileSave(input, NS("test")("save"), roots = volumes, session = session)
}
server <- function(input, output, session) {
callModule(saveModule, "test")
}
ui <- shinyUI(fluidPage(
# Taken straight from shinyFilesExample()
shinySaveButton(NS("test")("save"), 'Save file', 'Save file as...', filetype=list(text='txt', picture=c('jpeg', 'jpg'))))
)
runGadget(app = ui, server = server, viewer = dialogViewer(title = "test"))
This doesn't work correctly when trying to navigate folders or switch roots.
When pulling the code out of the callModule, it does work, but as I said, the actual use case has a rather large app and I don't want to clutter the app.R with code from a single module.
Is there any way to get shinyFiles to work correctly in a callModule context? Or some other viable work-around?
For reference, here is a working server:
server <- function(input, output, session) {
volumes <- getVolumes()
shinyFileSave(input, NS("test")("save"), roots = volumes, session = session)
}
with ui and runGadget as above.
Related
I am trying to build a front end using Shiny to pass some parameters to a markdown document. One parameter I want to pass is a user selected file name, including the path. I would like to use a file open type dialogue box for the user to navigate through directories and select the file. I do not need/want to actually open the file, that happens in the markdown file, I just want to pass the path and file name of the selected file out of the shiny app. I have this set up using fileInput() but of course this opens the file and makes a copy in a temp directory and the associated path is to the temp directory not the original directory. There have been some related questions about this and the only answers are that this is a security issue related to the server-based nature of shiny. Again, I don't want to open the file, just grab the original path and name. Any thoughts on how to achieve this? Here's the code stripped down to just this issue ...
library(shiny)
ui <- fluidPage(
titlePanel("Input"),
mainPanel(
fileInput(inputId = "rtffile", "Choose RTF File", accept = ".rtf", ),
)
server <- function(input, output, session) {
observe({
filename <<- input$rtffile
})
}
shinyApp(ui = ui, server = server)
In general, you can’t get a web browser to give you the path to a file on the
user’s local machine.
However, it’s possible to get a path to a file on the server.
If the server and the local machine happen to be the same, you can use e.g. shinyFiles to
pick a path:
library(shiny)
library(shinyFiles)
ui <- fluidPage(
titlePanel("Input"),
mainPanel(
shinyFilesButton("file", "Choose File", "Choose a file", multiple = FALSE),
verbatimTextOutput("file")
)
)
server <- function(input, output, session) {
roots <- getVolumes()
shinyFileChoose(input, "file", roots = roots)
file <- reactive(parseFilePaths(roots, input$file))
output$file <- renderPrint(file())
}
shinyApp(ui = ui, server = server)
Is it possible to see the changes in UI (only the UI and not server) after I make changes in my code, without having to restart the server/refresh the page? If cache needs to be cleared, can that also be done?
You could use reactiveFileReader to regularly source the code you'd save in another file, and renderUI()/uiOutput to display that dynamic UI:
app.R
library(shiny)
ui <- uiOutput("ui")
server <- function(input, output, session) {
ui <- reactiveFileReader(1000, session, "source.R", source)
output$ui <- renderUI(ui())
}
shinyApp(ui = ui, server = server)
source.R
fluidPage(
textInput("text", "text"),
textInput("text2", "text")
)
You still have to find out howto get rid of that "TRUE":
I have an R shiny app that requires multiple files (all found within the same folder) to be uploaded. I also know the names of the files to look for.
Is there any way to upload these files other than via fileInput(..., multiple = TRUE)? Ideally, I would have the user upload the whole directory.
You can use the library shinyFiles to let the user pic a folder somewhere on their local disk. Then in the server you can use the user input to load all the files that you need from this folder.
Example
library(shiny)
library(shinyFiles)
### UI
ui <- fluidPage(
shinyDirButton('directory_select', 'Select a directory', title='Select a directory'),
textOutput('directory_name')
)
### Server
server <- function(input, output, session) {
volumes <- getVolumes()
shinyDirChoose(input, 'directory_select', roots=volumes, session=session)
dirname <- reactive({parseDirPath(volumes, input$directory_select)})
## Observe input dir. changes
observe({
if(!is.null(dirname)){
print(dirname())
output$directory_name <- renderText(dirname())
## Load files here
# csv <- read.csv(paste0(dirname(), '/filename1.csv'))
# rdata <- load(paste0(dirname(), '/filename2.Rdata'))
# etc.
}
})
}
shinyApp(ui = ui, server = server)
Recently, I have made a lot of updates to one of the shinydashboards. Nothing too complex, but now the server script is becoming large and I wanted to modularize the code into sections. One thing is there a lot of user parameters and I wanted to place all of these in on script and source.
Sourced Code
mc_High_parameter <- function(input, output, session) {
output$High <- renderUI({
sliderInput("High", 'High Assumption:',
min = 0, max = .25,value = .1)
})
}
Calling on the Server side
callModule(mc_High_parameter,'High')
Displayed on the UI Side
uiOutput("High")
The App loads but the Slider is not displayed on the UI, I cannot really understand the issue. Any Suggestions?
Shiny modules take some getting used to, especially when working with renderUI. I'd take a look at this GitHub page to help give you a sense of how to put it all together, and also read the Shiny help page on the session object, which is fundamental to modules.
In your specific case, here are a few changes to get it running:
1. You need a UI module, which you hadn't included. This is where you call uiOutput.
mc_UI <- function(id) {
ns <- NS(id)
fluidPage(
uiOutput(ns("High"))
)
}
2. You need to call the UI module within the ui.
ui <- fluidPage(
fluidRow(
mc_UI("High")
)
)
3. You need to use session$ns() in the id of your sliderInput so that the namespace is matched properly from the UI.
mc_High_parameter <- function(input, output, session) {
output$High <- renderUI({
sliderInput(session$ns("High"), "High Assumption:",
min = 0, max = .25,value = .1)
})
}
4. Ensure you call session in your server function.
server <- function(input, output, session) {
callModule(mc_High_parameter,"High")
}
I want to make a Shiny App in which the user can press an actionbutton which would then trigger some code on the server side creating a file in the www folder and then opens/downloads the file.
Suppose the file is test.txt (in my case it would be a variety of R, Excel, and exe files which will be copied from different folders on a drive to the www folder).
My first try was to use the actionbutton with the onclick option as shown below
ui <- fluidPage(
actionButton("showtxt", "Show/Download File", onclick = "window.open('test.txt')")
)
server <- function(input, output, session){
observeEvent(input$showtxt,{
# Write some text
write.table(c("Test"), file = "www/test.txt")
})
}
shinyApp(ui=ui,server=server)
But this doesn't work since the onclick action is done before the observevent is evaluated.
I then tried to call a function inside to the onclick option as shown below
CreateFileAndLink <- function(){
write.table(c("Test"), file = "www/test.txt")
return("window.open('test.txt')")
}
ui <- fluidPage(
actionButton("showtxt", "Show/Download File", onclick = CreateFileAndLink())
)
server <- function(input, output, session){}
shinyApp(ui=ui,server=server)
This works but has the downside that now the file is created when upon opening the Shiny App as opposed to creating the file when the user clicks the actionbutton. This is very inefficient if I were to use this piece of code multiple times in an App with relatively large files.
Maybe it is possible to make sure that the observevent is executed before the onclick-action, or maybe use the onclick option on the server side.
Any help would be greatly appreciated!
Cheers
UPDATE:
I found out that the great shinyjs package by Dean Attali contains a onclick function that might be of help here. I tried to run the code below but it didn't work :/
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
actionButton("showtxt", "Show/Download File")
)
server <- function(input, output, session){
observeEvent(input$showtxt,{
# Write some text
write.table(c("Test"), file = "www/test.txt")
# Call Onclick
onclick("showtxt", "window.open('test.txt')")
})
}
shinyApp(ui=ui,server=server)
I found a solution using the onclick function from the shinyjs package.
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
actionButton("showtxt", "Show/Download File")
)
server <- function(input, output, session){
observeEvent(input$showtxt,{
# Write some text
write.table(c("Test"), file = "www/test.txt")
})
# Call Onclick
onclick("showtxt", runjs("window.open('test.txt')"))
}
shinyApp(ui=ui,server=server)