简体   繁体   中英

R shiny serve data to javascript sendCustomMessage not working

this is driving me crazy. I am using ag-grid directly as javascript package, as the wrapper rag-grid is outdated.

All the examples I looked at unfortunately are observing for a javascript event before sending a custom message, I don't need this as I just want to serve data at startup, but my code doesn't work, it returns:

Error in func(fname, ...) : app.R did not return a shiny.appobj object.

what I want to achieve at the end will be

  1. catch data from mysql with dbReadTable (DONE)
  2. serve data from R to javascript at startup
  3. render the initial values in the table
  4. observe for edits in javascript and pass them to R
  5. process them in R to pass them to mysql with dbExecute

however I am still stuck at point 2, trying to start the communication between backend and frontend.

This is the code.

app.R

library(shiny)
library(RSQLite)
library(DBI)
library(tidyverse)

myjson <- '[
  {
    "ID": 1,
    "REQCAT": "cat 1"
  },
  {
    "ID": 2,
    "REQCAT": "cat 2"
  }
]'


#initialise database
con <- dbConnect(RSQLite::SQLite(), ":memory:")
#starting status of database
start <- as.data.frame(fromJSON(myjson))
dbWriteTable(con, "requests", start)
#testing reading
dbReadTable(con, "requests")
#testing writing
dbExecute(con,
    'INSERT INTO requests 
      (ID,REQCAT) 
        VALUES (3,"cat 3")
    ;')
dbReadTable(con, "requests")


###################
#### START APP ####
###################

ui <- fluidPage(
  #This tells shiny to include both css and scripts of aggrid
  tags$script(src="https://unpkg.com/ag-grid-enterprise/dist/ag-grid-enterprise.min.js"),
  titlePanel("Ag-Grid Basic Example"),
  uiOutput("myGrid_container")
)

server <- function(input, output, session) {
  data <- dbReadTable(con, "requests")
  dataJSON<-toJSON(data,pretty=TRUE,auto_unbox=TRUE)
  session$sendCustomMessage("jsondata",dataJSON)
  #This tells shiny to run our javascript file "script.js" and send it to the UI for rendering
  output$myGrid_container<- renderUI({
    tagList(
      div(id = "myGrid", style="height: 200px; width:500px;", class="ag-theme-alpine"),
      tags$script(src="script.js")
    )
  })
}

shinyApp(ui = ui, server = server)

dbDisconnect(con)

script.js in www subfolder

const gridOptions = {
  columnDefs: [
    { field: "ID" },
    { field: "REQCAT" }
  ],
  defaultColDef: {
    flex: 1,
    editable: true,
  },
  rowData: getRows(),
  enableRangeSelection: true,
  enableFillHandle: true,
  undoRedoCellEditing: true,
  undoRedoCellEditingLimit: 5,
  enableCellChangeFlash: true,
  onFirstDataRendered: onFirstDataRendered,
  onCellValueChanged: onCellValueChanged,
};

function undo() {
  gridOptions.api.undoCellEditing();
}

function redo() {
  gridOptions.api.redoCellEditing();
}

function onFirstDataRendered() {
  setValue('#undoInput', 0);
  disable('#undoInput', true);
  disable('#undoBtn', true);

  setValue('#redoInput', 0);
  disable('#redoInput', true);
  disable('#redoBtn', true);
}

function onCellValueChanged(params) {
  var undoSize = params.api.getCurrentUndoSize();
  setValue('#undoInput', undoSize);
  disable('#undoBtn', undoSize < 1);

  var redoSize = params.api.getCurrentRedoSize();
  setValue('#redoInput', redoSize);
  disable('#redoBtn', redoSize < 1);
}

function disable(id, disabled) {
  document.querySelector(id).disabled = disabled;
}

function setValue(id, value) {
  document.querySelector(id).value = value;
}

function getRows() {
  Shiny.addCustomMessageHandler("jsondata", myfunction );
// this function is called by the handler, which passes the message
  function myfunction(message){
      const rowData = message;
      return rowData;
  }
}

// setup the grid after the page has finished loading
const gridDiv = document.querySelector('#myGrid');
new agGrid.Grid(gridDiv, gridOptions);

I also tried the option of having includeScript("www/script.js"), in the UI but I had no luck.

Any hint on how to proceed?

Thanks

$(document).ready(function () {

    // all your "helper" functions here

    function gridOptions(rowData) {
        return {
            columnDefs: [
                { field: "ID" },
                { field: "REQCAT" }
            ],
            defaultColDef: {
                flex: 1,
                editable: true,
            },
            rowData: rowData,
            enableRangeSelection: true,
            enableFillHandle: true,
            undoRedoCellEditing: true,
            undoRedoCellEditingLimit: 5,
            enableCellChangeFlash: true,
            onFirstDataRendered: onFirstDataRendered,
            onCellValueChanged: onCellValueChanged,
        };
    }

    const gridDiv = document.querySelector('#myGrid');

    Shiny.addCustomMessageHandler("jsondata", function (rowData) {
        new agGrid.Grid(gridDiv, gridOptions(rowData));
    });

});

and in the Shiny app, remove the renderUI and:

ui <- fluidPage(
  tags$head(
    tags$script(src = "https://unpkg.com/ag-grid-enterprise/dist/ag-grid-enterprise.min.js"),
    tags$script(src = "myscript.js")
  ),
  titlePanel("Ag-Grid Basic Example"),
  div(id = "myGrid", style = "height: 200px; width:500px;", class = "ag-theme-alpine")
)

I'm not familiar with this JavaScript library, but it would be strange that rowData has to be a JSON string, and you are giving a JSON string with this code, because you use toJSON . So either remove toJSON in the R code (Shiny automatically takes care of the conversion), or use JSON.parse in the JavaScript code. The toJSON method can be useful if the data passed to JavaScript has not the required format (you can control the format of a jsonified dataframe with toJSON ). I would first try without toJSON and use console.log to check the format. If the format is not the required one, I would switch to the toJSON method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM