Nothing
#' Connect a ggvis graphic to a shiny app.
#'
#' Embedding ggvis in a shiny app is easy. You need to make a place for it in
#' your \code{ui.r} with \code{ggvisOutput}, and tell your \code{server.r}
#' where to draw it with \code{bind_shiny}. It's easiest to learn by example:
#' there are many shiny apps in \code{demo/apps/} that you can learn from.
#'
#' @section Client-side:
#' In your UI, use \code{ggvisOutput()} in \code{ui.r} to insert an html
#' placeholder for the plot.
#'
#' If you're going to be using interactive controls generated by ggvis,
#' use \code{\link[shiny]{renderUI}()} to add a place holder. By convention,
#' if the id of plot placehold is called "plot", call the controls placeholder
#' "plot_ui".
#'
#' @section Server-side:
#' When you run ggvis plot interactively, it is automatically plotted because
#' it triggers the default print method. In shiny apps, you need to
#' explicitly render the plot to a specific placeholder with
#' \code{bind_shiny}:
#'
#' \code{p \%>\% bind_shiny("plot")}
#'
#' If the plot has controls, and you've reserved space for them in the UI,
#' supply the name of the placeholder as the third argument:
#'
#' \code{p \%>\% bind_shiny("plot", "plot_ui")}
#' @examples
#' ## Run these examples only in interactive R sessions
#' if (interactive()) {
#'
#' # Simplest possible app:
#' library(shiny)
#' runApp(list(
#' ui = bootstrapPage(
#' ggvisOutput("p"),
#' uiOutput("p_ui")
#' ),
#' server = function(..., session) {
#' mtcars %>%
#' ggvis(~wt, ~mpg) %>%
#' layer_points() %>%
#' layer_smooths(span = input_slider(0, 1)) %>%
#' bind_shiny("p", "p_ui")
#' }
#' ))
#'
#' }
#' @name shiny-ggvis
NULL
#' @rdname shiny-ggvis
#' @param vis A ggvis object, or a reactive expression that returns a ggvis
#' object.
#' @param session A Shiny session object.
#' @param ... Other arguments passed to \code{as.vega}.
#' @export
bind_shiny <- function(vis, plot_id, controls_id = NULL, ...,
session = shiny::getDefaultReactiveDomain()) {
validate_plot_id(plot_id)
if (is.null(session)) {
stop("bind_shiny() must be run inside a shiny app.", call. = FALSE)
}
if (shiny::is.reactive(vis)) {
visf <- vis
} else if (is.ggvis(vis)) {
visf <- function() vis
} else {
stop("bind_shiny requires a ggvis object or a reactive expression that returns a ggvis object",
call. = FALSE)
}
r_spec <- shiny::reactive({
as.vega(visf(), session = session, dynamic = TRUE, ...)
})
observe_spec(r_spec, plot_id, session)
observe_data(r_spec, plot_id, session)
exec_connectors(r_spec, plot_id, session)
if (!is.null(controls_id)) {
bind_shiny_ui(vis, controls_id, session = session)
}
vis
}
#' @param controls_id Unique identifier for controls div.
#' @rdname shiny-ggvis
#' @export
bind_shiny_ui <- function(vis, controls_id,
session = shiny::getDefaultReactiveDomain()) {
if (is.null(session)) {
stop("bind_shiny_ui() must be run inside a shiny app.", call. = FALSE)
}
if (shiny::is.reactive(vis)) {
visf <- vis
} else if (is.ggvis(vis)) {
visf <- function() vis
} else {
stop("bind_shiny_ui requires a ggvis object or a reactive expression that returns a ggvis object",
call. = FALSE)
}
shiny::observe({
controls <- visf()$controls
if (empty(controls)) return()
# Wrap each control in a div, for layout purposes
divs <- lapply(controls, htmltools::div, class = "ggvis-input-container")
session$output[[controls_id]] <- shiny::renderUI(htmltools::tagList(divs))
})
vis
}
# Tell an observer to suspend or resume, depending on the hidden state of an
# output object on the client.
<- function(obs, id, session) {
force(obs)
shiny::observe({
isHidden <- session$clientData[[paste0('output_', id, '_hidden')]]
if (identical(isHidden, FALSE)) {
obs$resume()
} else {
obs$suspend()
}
})
}
# Create an observer for a reactive vega spec
observe_spec <- function(r_spec, id, session) {
obs <- shiny::observe(suspended = TRUE, {
session$sendCustomMessage("ggvis_vega_spec", list(
plotId = id,
spec = r_spec()
))
})
(obs, id, session)
}
# Create observers for the data objects attached to a reactive vega spec
observe_data <- function(r_spec, id, session) {
# A list for keeping track of each data observer
data_observers <- list()
outer_obs <- shiny::observe(suspended = TRUE, {
# If data_observers list is nonempty, that means there are old observers
# which need to be suspended before we create new ones. This can happen when
# the reactive containing the ggvis() call is invalidated.
for (obs in data_observers) obs$suspend()
data_table <- c(attr(r_spec(), "data_table", TRUE),
attr(r_spec(), "scale_data_table", TRUE))
# Create observers for each of the data objects
data_observers <<- lapply(names(data_table), function(data_name) {
# The data_table list contains named objects. The names are synthetic IDs
# that are present in the vega spec.
force(data_name)
obs <- shiny::observe(suspended = TRUE, {
data_reactive <- data_table[[data_name]]
session$sendCustomMessage("ggvis_data", list(
plotId = id,
name = data_name,
value = as.vega(data_reactive(), data_name)
))
})
(obs, id, session)
obs
})
# Tell the plot to update _after_ all the data has been sent
data_observers[[length(data_observers) + 1]] <<- shiny::observe(suspended = TRUE, {
# Take dependency on all data objects
for (name in names(data_table)) {
data_table[[name]]()
}
session$sendCustomMessage("ggvis_command", list(
plotId = id,
command = "update"
))
}, priority = -1)
(data_observers[[length(data_observers)]], id, session)
})
(outer_obs, id, session)
}
# Run the connector functions
exec_connectors <- function(r_spec, plot_id, session) {
connectors <- shiny::isolate(attr(r_spec(), "connectors", TRUE))
lapply(connectors, function(connect) {
if (!is.null(connect)) {
connect(session, plot_id)
}
})
}
validate_plot_id <- function(id) {
if (grepl(".", id, fixed = TRUE)) {
stop("Plot ID '", id, "' is not valid. The ID must not contain a dot (.) character.")
}
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.