1---
2title: "Introduction to HTML Widgets"
3date: "`r Sys.Date()`"
4output:
5  html_document:
6    highlight: kate
7    toc: true
8    toc_depth: 4
9    mathjax: null
10vignette: >
11  %\VignetteIndexEntry{Introduction}
12  %\VignetteEngine{knitr::rmarkdown}
13  \usepackage[utf8]{inputenc}
14---
15
16## Overview
17
18The **[htmlwidgets](https://cran.r-project.org/package=htmlwidgets)** package provides a framework for creating R bindings to JavaScript libraries. HTML Widgets can be:
19
20* Used at the R console for data analysis just like conventional R plots.
21* Embedded within [R Markdown](https://rmarkdown.rstudio.com/) documents
22* Incorporated into [Shiny](https://shiny.rstudio.com/) web applications.
23* Saved as standalone web pages for ad-hoc sharing via email, Dropbox, etc.
24
25By following a small set of easy-to-follow conventions, it is possible to create HTML widgets with very little code. All widgets include the following components:
26
271. **Dependencies**. These are the JavaScript and CSS assets used by the widget (e.g. the library you are creating a wrapper for).
28
293. **R binding**. This is the function that end users will call to provide input data to the widget as well as specify various options for how the widget should render. This also includes some short boilerplate functions required to use the widget within Shiny applications.
30
313. **JavaScript binding**. This is the JavaScript code that glues everything together, passing the data and options gathered in the R binding to the underlying JavaScript library.
32
33HTML widgets are always hosted within an R package and should include all of the source code for their dependencies. This is to ensure that code which depends on widgets is fully reproducible (i.e. doesn't require an internet connection or the ongoing availability of an internet service to run).
34
35## Example (sigma.js)
36
37To start with we'll walk through the creation of a simple widget that wraps the [sigma.js](http://sigmajs.org) graph visualization library. When we're done we'll be able to use it to display interactive visualizations of [GEXF](https://gephi.org/gexf/format/) (Graph Exchange XML Format) data files. For example:
38
39```{r, eval=FALSE}
40library(sigma)
41data <- system.file("examples/ediaspora.gexf.xml", package = "sigma")
42sigma(data)
43```
44
45<img src="images/sigma.png" alt="sigma" data-toggle="tooltip" data-placement="right" title="" data-original-title="Note this is just an image of the visualization so it's not interactive. You can play with the interactive version by following the steps in the demo section below." onload="$(this).tooltip()">
46
47Note that the above is just an image of the visualization so it's not interactive. You can play with the interactive version by following the steps in the demo section below.
48
49There is remarkably little code required to create this binding. Below we'll go through all of the components step-by-step. Then we'll describe how you can create your own widgets (including automatically generating basic scaffolding for all of the core components).
50
51### File layout
52
53Let's assume that our widget is named **sigma** and is located within an R package of the same name. Our JavaScript binding source code file is named sigma.js. Since our widget will read GEXF data files we'll also need to include both the base sigma.min.js library as well as its GEXF plugin. Here are the files that we'll add to the package:
54
55```text
56R/
57| sigma.R
58
59inst/
60|-- htmlwidgets/
61|   |-- sigma.js
62|   |-- sigma.yaml
63|   |-- lib/
64|   |   |-- sigma-1.0.3/
65|   |   |   |-- sigma.min.js
66|   |   |   |-- plugins/
67|   |   |   |   |-- sigma.parsers.gexf.min.js
68```
69
70Note the convention that the JavaScript, YAML, and other dependencies are all contained within the `inst/htmlwidgets` directory (which will subsequently be installed into a package sub-directory named `htmlwidgets`).
71
72### Dependencies
73
74Dependencies are the JavaScript and CSS assets used by a widget. Dependencies are included within the `inst/htmlwidgets/lib` directory. Dependencies are specified using a YAML configuration file which uses the name of the widget as its base file name. Here's what our **sigma.yaml** file looks like:
75
76```yaml
77dependencies:
78  - name: sigma
79    version: 1.0.3
80    src: htmlwidgets/lib/sigma-1.0.3
81    script:
82      - sigma.min.js
83      - plugins/sigma.parsers.gexf.min.js
84```
85
86The dependency `src` specification refers to the directory that contains the library and `script` refers to specific JavaScript files. If your library contains multiple JavaScript files specify each one on a line beginning with `-` as shown here. You can also add `stylesheet` entries and even `meta` or `head` entries. Multiple dependencies may be specified in one YAML file. See the documentation on the `htmlDependency` function in the [**htmltools**](https://cran.r-project.org/package=htmltools) package for additional details.
87
88### R binding
89
90We need to provide users with an R function that invokes our widget. Typically this function will accept input data as well as various options that control the widget's display. Here's the R function for `sigma`:
91
92```r
93#' @import htmlwidgets
94#' @export
95sigma <- function(gexf, drawEdges = TRUE, drawNodes = TRUE,
96                  width = NULL, height = NULL) {
97
98  # read the gexf file
99  data <- paste(readLines(gexf), collapse="\n")
100
101  # create a list that contains the settings
102  settings <- list(
103    drawEdges = drawEdges,
104    drawNodes = drawNodes
105  )
106
107  # pass the data and settings using 'x'
108  x <- list(
109    data = data,
110    settings = settings
111  )
112
113  # create the widget
114  htmlwidgets::createWidget("sigma", x, width = width, height = height)
115}
116```
117
118The function takes two classes of input: the GEXF data file to render and some additional settings which control how it is rendered. This input is collected into a list named `x` which is then passed on to the `htmlwidgets::createWidget` function. This `x` variable will subsequently be made available to the JavaScript binding for sigma (this is described below). Any width or height parameter specified is also forwarded to the widget (widgets size themselves automatically by default so typically don't require an explicit width or height).
119
120We want our sigma widget to also work in Shiny applications, so we add the following boilerplate Shiny output and render functions (these are always the same for all widgets):
121
122```r
123#' @export
124sigmaOutput <- function(outputId, width = "100%", height = "400px") {
125  htmlwidgets::shinyWidgetOutput(outputId, "sigma", width, height, package = "sigma")
126}
127#' @export
128renderSigma <- function(expr, env = parent.frame(), quoted = FALSE) {
129  if (!quoted) { expr <- substitute(expr) } # force quoted
130  htmlwidgets::shinyRenderWidget(expr, sigmaOutput, env, quoted = TRUE)
131}
132```
133
134### JavaScript binding
135
136_**Note:** An older, less intuitive JavaScript binding API was used in htmlwidgets 0.5.2 and earlier, and continues to be supported in newer versions of htmlwidgets. See this [archived version](https://github.com/ramnathv/htmlwidgets/blob/f735840bf938d35d3c4143c0d16515da6ff252bd/develop_intro.Rmd#L128) for details on the legacy binding API. New widgets are encouraged to use the newer API described below._
137
138The third piece in the puzzle is the JavaScript required to activate the widget. By convention we'll define our JavaScript binding in the file `inst/htmlwidgets/sigma.js`. Here is the full source code of the binding:
139
140```javascript
141HTMLWidgets.widget({
142
143  name: "sigma",
144
145  type: "output",
146
147  factory: function(el, width, height) {
148
149    // create our sigma object and bind it to the element
150    var sig = new sigma(el.id);
151
152    return {
153      renderValue: function(x) {
154
155        // parse gexf data
156        var parser = new DOMParser();
157        var data = parser.parseFromString(x.data, "application/xml");
158
159        // apply settings
160        for (var name in x.settings)
161          sig.settings(name, x.settings[name]);
162
163        // update the sigma object
164        sigma.parsers.gexf(
165          data,          // parsed gexf data
166          sig,           // sigma object
167          function() {
168            // need to call refresh to reflect new settings and data
169            sig.refresh();
170          }
171        );
172      },
173
174      resize: function(width, height) {
175
176        // forward resize on to sigma renderers
177        for (var name in sig.renderers)
178          sig.renderers[name].resize(width, height);
179      },
180
181      // Make the sigma object available as a property on the widget
182      // instance we're returning from factory(). This is generally a
183      // good idea for extensibility--it helps users of this widget
184      // interact directly with sigma, if needed.
185      s: sig
186    };
187  }
188});
189```
190
191We provide a name and type for the widget, plus a `factory` function that takes `el` (the HTML element that will host this widget), `width`, and `height` (width and height of the HTML element, in pixels--you can always use `offsetWidth` and `offsetHeight` for this).
192
193The `factory` function should prepare the HTML element to start receiving values. In this case we create a new sigma element and pass it the `id` of the DOM element that hosts the widget on the page.
194
195We're going to need access to the sigma object later (to update its data and settings) so we save it as a variable `sig`. Note that variables declared directly inside of the factory function are tied to a particular widget instance/`el`.
196
197The return value of the `factory` function is called a _widget instance object_. It is a bridge between the htmlwidgets runtime, and the JavaScript visualization that you're wrapping. As the name implies, each widget instance object is responsible for managing a single widget instance on a page.
198
199The widget instance object you create must have one required method, and may have one optional method:
200
2011. The required `renderValue` method actually pours our dynamic data and settings into the widget's DOM element. The `x` parameter contains the widget data and settings. We parse and update the GEXF data, apply the settings to our previously-created `sig` sigma object, and finally call `refresh` to reflect the new values on-screen. This method may be called repeatedly with different data (i.e. in Shiny), so be sure to account for that possibility. If it makes sense for your widget, consider making your visualization transition smoothly from one value of `x` to another.
202
2032. The optional `resize` method is called whenever the element containing the widget is resized. The only reason not to implement this method is if your widget naturally scales (without additional JavaScript code needing to be invoked) when its element size changes. In the case of sigma.js, we forward the sizing information on to each of the underlying sigma renderers.
204
205All JavaScript libraries handle initialization, binding to DOM elements, dynamically updating data, and resizing slightly differently. Most of the work on the JavaScript side of creating widgets is mapping these three functions---`factory`, `renderValue`, and `resize`---correctly onto the behavior of the underlying library.
206
207The sigma.js example uses a simple object literal to create its widget instance object, but you can also use [class based objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) or any other style of object, as long as `obj.renderValue(x)` and `obj.resize(width, height)` can be invoked on it.
208
209You can add additional methods and properties on the widget instance object. Although they won't be called by htmlwidgets itself, they might be useful to users of your widget that know some JavaScript and want to further customize your widget by adding custom JS code (e.g. using the `htmlwidgets::onRender` R function). In this case we add an `s` property to make the sigma object itself available.
210
211### Demo
212
213Our widget is now complete! If you want to test drive it without reproducing all of the code locally you can install it from GitHub as follows:
214
215```r
216devtools::install_github('jjallaire/sigma')
217```
218
219Here's the code to try it out with some sample data included with the package:
220
221```r
222library(sigma)
223sigma(system.file("examples/ediaspora.gexf.xml", package = "sigma"))
224```
225
226If you execute this code in the R console you'll see the widget displayed in the RStudio Viewer (or in an external browser if you aren't running RStudio). If you include it within an R Markdown document the widget will be embedded into the document.
227
228We can also use the widget in a Shiny application:
229
230```r
231library(shiny)
232library(sigma)
233
234gexf <- system.file("examples/ediaspora.gexf.xml", package = "sigma")
235
236ui = shinyUI(fluidPage(
237  checkboxInput("drawEdges", "Draw Edges", value = TRUE),
238  checkboxInput("drawNodes", "Draw Nodes", value = TRUE),
239  sigmaOutput('sigma')
240))
241
242server = function(input, output) {
243  output$sigma <- renderSigma(
244    sigma(gexf,
245          drawEdges = input$drawEdges,
246          drawNodes = input$drawNodes)
247  )
248}
249
250shinyApp(ui = ui, server = server)
251```
252
253## Creating your own widgets
254
255### Requirements
256
257To implement a widget you need to create a new R package that in turn depends on the **htmlwidgets** package. You can install the package from CRAN as follows:
258
259```r
260install.packages("htmlwidgets")
261```
262
263While it's not strictly required, the step-by-step instructions below for getting started also make use of the **devtools** package which you can also install from CRAN:
264
265```r
266install.packages("devtools")
267```
268
269### Scaffolding
270
271To create a new widget you can call the `scaffoldWidget` function to generate the basic structure for your widget. This function will:
272
273* Create the .R, .js, and .yaml files required for your widget;
274
275* If provided, take a [Bower](https://bower.io/) package name and automatically download the JavaScript library (and its dependencies) and add the required entries to the .yaml file.
276
277This method is highly recommended as it ensures that you get started with the right file structure. Here's an example that assumes you want to create a widget named 'mywidget' in a new package of the same name:
278
279```r
280devtools::create("mywidget")               # create package using devtools
281htmlwidgets::scaffoldWidget("mywidget")    # create widget scaffolding
282devtools::document()                       # roxygenize, so NAMESPACE is updated
283devtools::install()                        # install the package so we can try it
284```
285
286This creates a simple widget that takes a single `text` argument and displays that text within the widgets HTML element. You can try it like this:
287
288```r
289library(mywidget)
290mywidget("hello, world")
291```
292
293This is the most minimal widget possible and doesn't yet include a JavaScript library to interface to (note that `scaffoldWidget` can optionally include JavaScript library dependencies via the `bowerPkg` argument). Before getting started with development you should review the introductory example above to make sure you understand the various components and also review the additional articles and examples linked to in the next section.
294
295### Learning more
296
297#### Additional articles
298
299There are additional articles that cover more advanced ground:
300
301* [HTML Widget Sizing](develop_sizing.html) explains custom sizing policies and when you might need to use them and describes implementing a `resize` method within JavaScript bindings.
302
303* [HTML Widgets: Advanced Topics](develop_advanced.html) describes framework features that support per-widget instance data, data transformations (e.g. converting a data frame into a d3 dataset), and providing widget options that are live JavaScript objects (e.g. function definitions).
304
305The Sizing article is particularly important as most JavaScript libraries require some additional interaction to keep their size synchronized with their containing element.
306
307#### Examples
308
309Studying the code of other packages is a great way to learn more about creating widgets:
310
3111. The [networkD3](https://github.com/christophergandrud/networkD3) package illustrates creating a widget on top of [D3](https://d3js.org/), using a custom sizing policy for a larger widget, and providing multiple widgets from a single package.
312
3132. The [dygraphs](https://github.com/rstudio/dygraphs/) package illustrates using widget instance data, handling dynamic re-sizing, and using [magrittr](https://github.com/tidyverse/magrittr) to decompose a large and flat JavaScript API into a more modular and pipeable R API.
314
3153. The [sparkline](https://github.com/htmlwidgets/sparkline) package illustrates  providing a custom HTML generation function (since sparklines must be housed in `<span>` rather than `<div>` elements).
316
317#### Questions and issues
318
319If you have questions about developing widgets or run into problems during development please don't hesitate to [post an issue](https://github.com/ramnathv/htmlwidgets/issues) on the project's GitHub repository.
320
321