Display html widget from plumber API in shiny application
Asked Answered
A

1

6

I'm trying to display an interactive graph requested through a plumber API and display it in a shiny application. I can't figure out how to make it work, using for example highcharter. My example application with a base plot and highcharter graph using an api is below.

I have the api working but does anyone know how to parse the htmlwidget input for display?

Thanks for the help!

Example api, start using

library(plumber)
r <- plumb("api.R") 
r$run(port=8000)

api.R

#' Plot out data from the iris dataset
#' @param spec If provided, filter the data to only this species (e.g. 'setosa')
#' @get /plot
#' @png
function(spec){
  myData <- iris
  title <- "All Species"

  # Filter if the species was specified
  if (!missing(spec)){
    title <- paste0("Only the '", spec, "' Species")
    myData <- subset(iris, Species == spec)
  }

  plot(myData$Sepal.Length, myData$Petal.Length,
       main=title, xlab="Sepal Length", ylab="Petal Length")
}

#' Plot the iris dataset using interactive chart
#' 
#' @param spec Species to filter
#'
#' @get /highchart
#' @serializer htmlwidget
function(spec){
  library(highcharter)

  myData <- iris
  title <- "All Species"

  # Filter if the species was specified
  if (!missing(spec)){
    title <- paste0("Only the '", spec, "' Species")
    myData <- subset(iris, Species == spec)
  }

  hchart(myData, "scatter", hcaes(x = Sepal.Length, y = Petal.Length, group = Species)) %>%
    hc_title(text = title)
}

app.R

# Application
library(shiny)
library(shinyjs)
library(shinydashboard)
library(httr)
library(grid)
library(ggplot2)

ui <- dashboardPage(
  dashboardHeader(title = "Image and Widget", titleWidth = 300),
  dashboardSidebar(disable = TRUE),
  dashboardBody(
    useShinyjs(), 
    fluidRow(
      column(width = 6, 
             shinydashboard::box(width = 12, 
                                 htmlOutput("species_selector"), 
                                 actionButton(inputId = "filter_action", label = "Filter", icon("search"), 
                                              style = "color: #fff; background-color: #337ab7; border-color: #2e6da4") 
             ) 
      ) 
    ), 
    fluidRow(
      column(width = 6, 
             shinyjs::hidden( 
               div(id = "iris_chartbox",
                   shinydashboard::tabBox(width = 12, 
                                          tabPanel(title = "Iris Base Plot", width = 12, 
                                                   imageOutput("iris_base_plot")
                                          ), 
                                          tabPanel(title = "Iris highchart", width = 12, 
                                                   uiOutput("iris_highchart")
                                          )
                   )
               ) 
             ) 
      )
    )
  )
)

server <- function(input, output) {

  # Make product line selector ----
  output$species_selector <- renderUI({ 
    selectInput( 
      inputId = "species_chosen",  
      label = "Species Chosen", 
      choices = c("setosa", "virginica", "versicolor")
    )
  })

  # Observe button click ----
  observeEvent(input$filter_action, { 
    # Make iris graph ----
    output$iris_base_plot <- renderImage({

      # A temp file to save the output. It will be deleted after renderImage
      # sends it, because deleteFile=TRUE.
      outfile <- tempfile(fileext = '.png')

      # Generate a png
      png(outfile, width = 400, height = 400)
      get_iris_base_plot(spec = input$species_chosen)
      dev.off()

      # Return a list
      list(src = outfile,
           alt = "This is alternate text") 
    }, deleteFile = TRUE)

    # Make iris highcharter graph ----
    output$iris_highchart <- renderUI({

      # Get the image
      interactive_graph <- get_iris_highchart(spec = isolate(input$species_chosen))

      return(interactive_graph)
    })

    shinyjs::show("iris_chartbox")
  })
}

# Function to make base plot graph ----
get_iris_base_plot <- function(spec) {
  req <- GET(URLencode(paste0("http://127.0.0.1:8000/plot?spec=", spec)))

  # Parse the request
  img_content <- httr::content(req, type = "image/png")

  # Visualise
  grid.raster(img_content) 
}

# Function to make highchart graph ----
get_iris_highchart <- function(spec) {
  my_req <- GET(URLencode(paste0("http://127.0.0.1:8000/highchart?spec=", spec)))

  # Parse the request
  req_content <- httr::content(my_req, type = "text/html; charset=utf-8")

  # Visualise
  req_content
}

shinyApp (ui, server)
Anyone answered 9/10, 2018 at 13:24 Comment(3)
I think the main problem here is to find how to visualize the XML document created by the get_iris_highchart() function. I am not sure if there is any source on this, and may be better to look at httr library documentation.Advert
The problem is that you can't just throw the HTML from the htmlwidget request inline in your Shiny UI. if you take a look at the source, it's going to have things like <html> tags, a <head> tag, and it's going to require supplemental JavaScript using <script> tags. Those are all things that are hard to just throw into your existing HTML page and have it work. Instead, you'll likely want to put them in an iframe, which lets you embed pages inside of pages. Try <iframe width="200" height="200" src=http://127.0.0.1:8000/highchart"> directly in the Shiny UI to get things started.Bonbon
For the reference, the same (or similar) question on plumber git: github.com/trestletech/plumber/issues/317Skyline
S
2

I managed to render a plumber-produced widget in a Shiny app, based on the idea from this answer: https://github.com/trestletech/plumber/issues/254. All what was required is to insert a html object tag into Shiny UI:

tags$html(HTML('<object data="<LINK TO YOUR WIDGET HERE>" width="100%" height="500px" type="text/html"> </object>')

Note that this doesn't work in RStudio viewer. It worked in Chrome (v71.0.3578.98) but not in Edge or IE.

Skyline answered 23/1, 2019 at 19:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.