Drag and release data into the interactive shiny application

Looking to incorporate drag and drop functionality into a shiny app? I've been able to successfully drag and drop data into an area and read it using JavaScript, but the challenge remains in having shiny effectively recognize the dragged data so that it can be processed on the server. Below is an example setup demonstrating this concept -- as there doesn't appear to be a built-in JavaScript function for drag-n-drop.

When executed, the application should resemble the following after dropping a dataset named "dat.csv". The objective is to store the drag-and-dropped data in a variable within input for subsequent processing in R. https://i.sstatic.net/9eLxS.png

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")),
    h3(id="data-title", "Drop Datasets"),
    div(class="col-xs-12", id="drop-area", ondragover="dragOver(event)", 
      ondrop="dropData(event)"),
    tableOutput('table'),  # currently non-functional

    ## 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")  # display the data
  )
)

server.R

## Create 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 does not exist
}

www/getdata.js

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);
};

www/css/styles.css

#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;
}

Answer №1

To incorporate the desired functionality in your project, insert the following snippet into the JavaScript file:

datasets[file.name.toLowerCase()] = e.target.result;
# Don't forget to include this line
Shiny.onInputChange("mydata", datasets);

After implementing this code, you will be able to reference input$mydata within your server-side logic. Make sure to note that it returns a list, so looping through is required (this is particularly crucial if handling multiple files).

Here's the complete code (which enables the display of multiple CSV files – please bear in mind that dropping duplicate filenames only shows one of them):

getdata.js (include one additional line as indicated)

styles.css (no modifications needed)

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]]))
    }
  })
}

Answer №2

Since August 2017, the functionality is now incorporated directly into fileInput from the amazing shiny!

Check out the blog post that introduces this feature here

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issue with Apollo Client - Mutation failing due to undefined property 'data'

When attempting to call a mutation on the client side in Next.js, I encounter an error every time I use the useMutation hook or the client itself, resulting in the following error: Cannot read property 'data' of undefined. See the code snippets b ...

Trouble invoking npm package.json scripts

The package.json file in my projects directory contains the following scripts section: "scripts": { "seed": "node bin/seed", "test": "echo \"Error: no test specified\" && exit 1" }, Executing $ npm test results in the followin ...

The process of fetching individual data values (integers) from a database using ajax

I am trying to use AJAX to retrieve data from a database. However, the code I have written is not giving me the results separately as needed. $(document).ready(function(){ $('#bathroom-select').change(function(){ var bathroom_optio ...

The resolve in Angular UI-Router is not being properly injected into the controller

As a Java developer venturing into the world of Angular and Express, I am encountering challenges along the way. To gain hands-on experience, I am attempting to create a small application using these technologies. The Goal: I have provided a password rese ...

`No valid form submission when radio buttons used in AngularJS`

Within my form, I have a single input text field that is required (ng-required="true") and a group of radio buttons (each with ng-model="House.window" and ng-required="!House.window"). Interestingly, I've discovered that if I select a radio button fir ...

I am confused by the combined output of the Array method and join function in JavaScript

In my JavaScript code, I declared the myArr variable in the following way: var myArr= Array(3); Upon logging the value of myArr, I received the output: [undefined × 3] Next, I utilized the JavaScript join function with the code snippet: myArr.join(&a ...

What could be causing my Discord bot to remain offline even when I run the command node main.js?

After successfully installing node.js, I proceeded to type the command 'npm init' in the command prompt and then installed discord.js. However, upon installation of discord.js, a 'node_modules' folder was not added inside the project, w ...

Displaying an undefined array result when combining it within a Promise in Angular 4

Here's a function I have: filterAllComponent(inputdata) { let a=[], b=[],c=[]; a= this.getfilterPlaces(inputdata); b= this.getfilterTransporter(inputdata); c= this.getfilterVehicles(inputdata); let getplaceArray = [], getTransp ...

Generate a grid to represent the pairings of two sets of vectors

Currently working on a project in R where I am dealing with two vectors, A and B, of the same length. My goal is to create a matrix from these vectors that captures the unique combinations of terms. For instance, if vector A is A <- c(1,1,1,2,3) and ve ...

The table is failing to display correctly due to an invalid class name being assigned

Everything is working smoothly on IE8, but seems to encounter issues on IE9 and the latest version of Chrome. I have a hunch that the problem lies in using undefined classnames as markers for selecting specific elements, similar to how an element's I ...

When a Vue.js Directive is inserted or bound, it actually takes precedence over the click event

Imagine having multiple dropdowns or elements on the page that all utilize a directive called "closable". This directive triggers an expression if the element clicked is outside of the element using the directive. The intended behavior is that when clicki ...

I encountered an issue when attempting to kick a user on Discord using a bot, as an error message was

Whenever I attempt to use my custom bot to kick someone, it keeps throwing an error. I closely followed CodeLyons' ban and kick tutorial from his 2020 discord bot playlist. The persistent error message is as follows: (node:5108) DeprecationWarning: T ...

express routes are failing to execute the function

When I navigate to routes in my browser, the results are displayed in my console but the server call seems to be running for a long time in the network. Can anyone provide some assistance? My controller, var express = require('express'); var ...

Issue with Google Maps Angular directive failing to function correctly

Trying to implement the Google Maps for AngularJS directive within a function has been a challenge. I've managed to make it work by moving $scope.map outside the function call and setting the lat/lon statically. However, my goal is to dynamically set ...

Using React Native - Issue occurs when tapping a row within a ListView: Property `_pressRow` cannot be read as it is null

Currently, I am working on a small ReactNative + Redux application that utilizes a ListView. While referencing the code example from the documentation available at here, I have implemented a setup similar to the provided sample. However, I am encountering ...

Comparing values in React Hooks and Socket.IO before updating

Is there a way to compare the values received from the socket with the state before updating it? The socket sends me a new location every second, but I only want to update the state when the latitude or longitude has changed, or every couple of seconds. ...

Retrieve the value of a child component that has been dynamically generated

I decided to create a custom component that includes a MUI TextField nested inside a MUI card. import * as React from 'react'; import Box from '@mui/material/Box'; import Card from '@mui/material/Card'; import CardContent from ...

Steps to dynamically modify an HTML element within an Android WebView

https://www.bing.com/search?q=кму here I want to switch the bing icon to google I attempted : if (url == "https://www.bing.com/search?q=") { view.evaluateJavascript( ("\n"+ "wind ...

Running a JavaScript file from Docker to fill a MongoDB database is not working when using the WSL shell on Windows 10

I'm facing some issues while trying to populate my MongoDB using a script. Every time I run the script, I encounter errors even though the Docker container is up and running. For reference, I'm on Windows 10 using WSL shell. https://i.stack.img ...

Ajax does not seem to be functioning properly with the hide feature

Although I may not be well-versed in HTML, AJAX, or JavaScript, I found myself in a situation where I needed to develop a script. The issue I am facing is that the "hide" function is not functioning properly in my AJAX code. Specifically, I have two text f ...