rstudio / plumber

Turn your R code into a web API.
https://www.rplumber.io
Other
1.39k stars 256 forks source link

How to display HTML widget in shiny application #317

Open tonyk7440 opened 5 years ago

tonyk7440 commented 5 years ago

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 is there any example on how to parse the htmlwidget input for display?

Example below:

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)
shizidushu commented 5 years ago

serializer htmlwidget will return a html. You may try to use iframe tag. I don't recommend this method because the html file is usually too large and takes long time to fetch.

You can do all work in shiny app. see highcharter docs. It works great!

If you want to use Plumber to do some of your work and use Shiny to do the rest of part, you may consider two paths

  1. Prepare the data in Plumber. Fetch the data and build the plot in Shiny or other web framework.
  2. Build the plot and extract the plot options in Plumber. Render the plot in your web framework.
kmaheshkulkarni commented 5 years ago

Hi, @tonyk7440 @trestletech @schloerke you should try this solution. I was just post it on StackOverflow and the solution is verified by StackOverflow. Click on the below link...

https://stackoverflow.com/questions/55211560/making-plumber-api-available-over-internet/56150308#56150308

thanks Mahesh Kulkarni Follow Me on Github: https://github.com/maheshKulkarniX