Search code examples
javascriptjqueryrshinyplotly

Run R shiny plotting function with JS in an R Shiny environment


I have a shiny app which adds a gridstack.js gridstack panel to the app whenever I click a button. Now, I would like to add a plot to the panel. Here is my code:

library(shinydashboard)
library(shiny)
library(shinyjs)
library(shinyjqui)
library(gridstackeR)
library(plotly)

ui <- fluidPage (
  useShinyjs(),
  includeCSS("www/app.css"),
  tags$head(
    tags$script(src = "https://cdnjs.cloudflare.com/ajax/libs/gridstack.js/10.0.1/gridstack-all.min.js", type='module'),
    tags$script(HTML('
      function addFirstWidget() {
        var grid = document.querySelector(\'.grid-stack\').gridstack;
        grid.addWidget({h:5, w: 2, content: \'This is where the plot goes\'});
      }
    '))
          
  ),
  grid_stack(id="mygrid"),
  actionButton("first_widget", "First Widget"),
  
  title = "Gridstack Prototype"
)

server <- function(input, output, session) {
  observeEvent(input$first_widget, {
    # browser()
    runjs("addFirstWidget();")
  })
  
}

shinyApp(ui = ui, server = server)

The JS Function addFirstWidget() makes a new gridstack panel whenever the button is pressed.

I want to call a function that renders a plot in plotly, ggplot, or some other plotting library within the panel(I'll know what library I use for the plot), for instance, this:

plot_ly(data = iris, x = ~Sepal.Length, y = ~Petal.Length)

However, I'm not sure how to go about actually putting this interactive plot into the panels. The interactivity must be kept, and I can't use JS to generate the plots


Solution

  • This should work:

    library(shinydashboard)
    library(shiny)
    library(shinyjs)
    library(shinyjqui)
    library(gridstackeR)
    library(plotly)
    ui <- fluidPage (
      useShinyjs(),
      includeCSS("www/app.css"),
      tags$head(
        tags$script(src = "https://cdnjs.cloudflare.com/ajax/libs/gridstack.js/10.0.1/gridstack-all.min.js", type='module'),
        tags$script(HTML('
          let numFirstWidgets = 0;
          function addFirstWidget() {
            var grid = document.querySelector(\'.grid-stack\').gridstack;
            let ids = "widget" + numFirstWidgets;
            grid.addWidget(`<div class="grid-stack-item"><div class="grid-stack-item-content" id=${ids}></div></div>`, {h:3, w: 2});
            numFirstWidgets += 1;
          }
        '))
      ),
      grid_stack(id="mygrid"),
      actionButton("first_widget", "First Widget"),
      # plotlyOutput("plot"),
      title = "Gridstack Prototype"
    )
    
    server <- function(input, output, session) {
      numOfWidgets <- 0
      observeEvent(input$first_widget, {
        # browser()
        runjs("addFirstWidget();")
        insertUI(
          paste0("#widget", numOfWidgets),
          where = "afterBegin",
          plotlyOutput(paste0("plotly", numOfWidgets), height="100%"),
          output[[paste0("plotly", numOfWidgets)]] <- renderPlotly({
            plot_ly(data = iris, x = ~Sepal.Length, y = ~Petal.Length)
          })
        )
        numOfWidgets <<- numOfWidgets + 1
      })
      
      
    }
    
    shinyApp(ui = ui, server = server)
    
    

    Basically it sets the actual id of div inside of the widget to something that the insertUI looks for. So now you can insert an unlimited number of items of this widget