How can I drag and drop data into a shiny app? I can drag and drop into an area and read it with javascript, but I'm not sure how to have shiny register it so I can process it on the server. Here is an example setup -- it's kinda long b/c I don't think there is a javascript function builtin to deal with drag-n-drop.
It currently should look like the following when run and a dataset "dat.csv" is dragged in. The goal is register the data that has been drag-n-dropped into a variable in input
so it can be processed in R.
library(shiny) ui <- shinyUI( fluidPage( tags$head(tags$link(rel="stylesheet", href="css/styles.css", type="text/css"), tags$script(src="getdata.js")), h3(id="data-title", "Drop Datasets"), div(class="col-xs-12", id="drop-area", ondragover="dragOver(event)", ondrop="dropData(event)"), tableOutput('table'), # doesn't do anything now ## debug div(class="col-xs-12", tags$hr(style="border:1px solid grey;width:150%"), tags$button(id="showData", "Show", class="btn btn-info", onclick="printData('dat.csv')")), div(id="data-output") # print the data ) )
## Make a sample dataset # write.csv(data.frame(a=1:10, b=letters[1:10]), "dat.csv", row.names=FALSE) server <- function(input, output, session) { output$table <- renderTable(input$data) # this variable doesn't exist }
var datasets = {}; var dragOver = function(e) { e.preventDefault(); }; var dropData = function(e) { e.preventDefault(); handleDrop(e.dataTransfer.files); }; var handleDrop = function(files) { for (var i = 0, f; f = files[i]; i++) { var reader = new FileReader(); reader.onload = (function(file) { return function(e) { datasets[file.name.toLowerCase()] = e.target.result; var div = document.createElement("div"); var src = "https://cdn0.iconfinder.com/data/icons/office/512/e42-512.png"; div.id = "datasets"; div.innerHTML = [ "<img class='thumb' src='", src, "' title='", encodeURI(file.name), "'/>", "<br>", file.name, "<br>"].join(''); document.getElementById("drop-area").appendChild(div); }; })(f); reader.readAsText(f); } }; // debug var printData = function(data) { var div = document.createElement("div"); div.innerHTML = datasets[data]; document.getElementById("data-output").appendChild(div); };
#data-title { text-align:center; } #drop-area { background-color:#BCED91; border:2px solid #46523C; border-radius:25px; height:90px; overflow:auto; padding:12px; } #drop-area #datasets { display:inline-block; font-size:small; margin-right:8px; text-align:center; vertical-align:top; } .thumb { height:45px; }
To add an input in a Shiny app, we need to place an input function *Input() in the ui object. Each input function requires several arguments. The first two are inputId , an id necessary to access the input value, and label which is the text that appears next to the input in the app.
You need an R server in order to execute a shiny app. There is no way to convert it into "pure HTML" and run the interactivity via javascript. The reason for that is that shiny apps will have to execute R code at runtime and javascript does not know how to deal with that.
You might be eager to deploy your Shiny app to a remote server. But the simplest way to run a Shiny app is to run it locally. You only need the shiny R package installed, and you can run the app in your browser.
You just need to add the following line into the js file
datasets[file.name.toLowerCase()] = e.target.result; # Add this line Shiny.onInputChange("mydata", datasets);
Then you can use input$mydata
in the server code. Note that it's a list, so you'll need to iterate through it (also necessary if you plan to drop several files).
Complete code (that also displays multiple csv files, note that if you drop multiple files with the same name, only one will be displayed):
getdata.js (add one line as above)
styles.css (no change)
ui.R
library(shiny) ui <- shinyUI( fluidPage( tags$head(tags$link(rel="stylesheet", href="css/styles.css", type="text/css"), tags$script(src="getdata.js")), sidebarLayout( sidebarPanel( h3(id="data-title", "Drop Datasets"), div(class="col-xs-12", id="drop-area", ondragover="dragOver(event)", ondrop="dropData(event)") ), mainPanel( uiOutput('tables') ) ) ) )
server.R
server <- function(input, output, session) { observeEvent(input$mydata, { len = length(input$mydata) output$tables <- renderUI({ table_list <- lapply(1:len, function(i) { tableName <- names(input$mydata)[[i]] tableOutput(tableName) }) do.call(tagList, table_list) }) for (name in names(input$mydata)) { output[[name]] <- renderTable(read.csv(text=input$mydata[[name]])) } }) }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With