1

I'm trying to load data from a CSV file using a javascript inline code and then render it in Shiny.

This is part of a much larger environment in javascript, otherwise I would simply read the file with read.csv() and render it with renderDataTable()). The CSV file is located in the www directory.

According to the browser DevTools/Console, the data is loaded in console but I get this error:

Uncaught TypeError: Shiny.setInputValue is not a function

Thus, nothing is loaded into input$mapInR and nothing is displayed.

The idea is to see, in the UI, "Reading file" and then a table with the CSV data, not just load the data in console or use read.csv("this_data.csv") to read the file. I have the latest Shiny and RStudio versions.

library(shiny)
library(DT)

ui <- fluidPage(
  tags$div(
    tags$html("Reading file"),
    # --- [1] This part should put the result of reading 'this_data.csv' into map
    tags$script(HTML("
         function getData(){
              return fetch('this_data.csv')
               .then(response => response.text())
               .then(csvString => {
                // Split the csv into rows
                   const rows = csvString.split('\\n');
                   for (row of rows) {
                      // Split the row into each of the comma separated values
                         console.log(row.split(','));
                   }
                                  })
                             } // closes getData
                             
        // getData();
        var map = getData();
        //console.log(map);
        
        // --- [2] Here input$mapInR <- map, but get 'Error: Shiny.setInputValue is not a function'
        Shiny.setInputValue('mapInR', map)
         "))
    ),
  # --- [4] Finally, display the table
  DT::dataTableOutput("table")
)

server <- function(input, output, session) {
  # --- [3] This portion gets input$mapInR and renders the table
  output$table <- DT::renderDataTable({
    data.frame(input$mapInR)
    })
}
  shinyApp(ui, server)

This is this_data.csv:

Date,Open,High,Low,Close
1/2/2007,50.03978,50.11778,49.95041,50.11778
1/3/2007,50.2305,50.42188,50.2305,50.39767
1/4/2007,50.42096,50.42096,50.26414,50.33236
1/5/2007,50.37347,50.37347,50.22103,50.33459
1/6/2007,50.24433,50.24433,50.11121,50.18112
1/9/2007,49.99489,49.99489,49.80454,49.91333
1/10/2007,49.91228,50.13053,49.91228,49.97246
1/11/2007,49.88529,50.2391,49.88529,50.2391
user438383
  • 5,716
  • 8
  • 28
  • 43
PLA
  • 89
  • 1
  • 7

1 Answers1

1

There are several things to address in this question.

  1. You get the Shiny.setInputValue is not a function error because Shiny is not ready when you want to use the function. You can wait for Shiny to be ready by inserting all your javascript code in this:

     $(document).on('shiny:connected', function() { 
         //javascript 
     });
    
  2. You can access the array of values and use the Shiny.setInputValue() function directly inside the promise or else you won't be able to retrieve the promise values (as you tried with var map = getData();).

  3. The easiest way to get data in the right format to handle it with R/Shiny is to convert the csv data to an array.

  4. You need to transform the array to a JSON string using the JSON.stringify() function to pass it to Shiny.setInputValue(), and retrieve it in Shiny with the fromJSON() function from the jsonlite package.

  5. Use req(input$mapInR) to wait until the data is loaded/converted and the input$mapInR created, before rendering the table.

  6. You might need to tweak the code below if you want to use values from the table for calculations, because all columns are of character type (as you can test with the print(str(map[,3])) line.

Here's the resulting code:

library(shiny)
library(DT)
library(jsonlite)
library(data.table)

ui <- fluidPage(
  tags$div(
    tags$html("Reading file"),
    # --- [1] This part should put the result of reading 'this_data.csv' into map
    tags$script(HTML("
    $(document).on('shiny:connected', function() {
         
         // function to convert csv data to an array
         const csv2json = (str, delimiter = ',') => {
            const titles = str.slice(0, str.indexOf('\\n')).split(delimiter);
            const rows = str.slice(str.indexOf('\\n') + 1).split('\\n');
            return rows.map(row => {
              const values = row.split(delimiter);
              return titles.reduce((object, curr, i) => (object[curr] = values[i], object), {})
            });
          };
              fetch('this_data.csv')
               .then(response => response.text())
               .then(csvString => {
                  let values = csv2json(csvString, ',');
                  var map = JSON.stringify(values);
                  Shiny.setInputValue('mapInR', map);
                });
                
                  })
   
         "))
  ),
  # --- [3] Finally, display the table
  DT::dataTableOutput("table")
)

server <- function(input, output, session) {
  # --- [2] This portion gets input$mapInR and renders the table
  output$table <- DT::renderDataTable({
    req(input$mapInR)
    map <- jsonlite::fromJSON(input$mapInR)
    map <- as.data.table(map)
  #  print(str(map[,3]))
    map
  })
}
shinyApp(ui, server)
julien.leroux5
  • 969
  • 7
  • 17
  • Very nice, @julien.leroux5 - I'm still prevented from voting, but the answer has been recorded as _useful_, as indeed it is. – PLA Aug 29 '21 at 18:09