Code Monkey home page Code Monkey logo

htmlwidgets's Introduction

HTML Widgets for R

R build status CRAN status Lifecycle: stable

The htmlwidgets package provides a framework for easily creating R bindings to JavaScript libraries. Widgets created using the framework can be:

  • Used at the R console for data analysis just like conventional R plots (via RStudio Viewer).
  • Seamlessly embedded within R Markdown documents and Shiny web applications.
  • Saved as standalone web pages for ad-hoc sharing via email, Dropbox, etc.

There are already several R packages based on htmlwidgets, including:

  • leaflet -- Interactive maps with OpenStreetMap
  • dygraphs --- Interactive time series visualization
  • networkD3 --- Network visualization with D3
  • sparkline --- Small inline charts
  • DT --- Tabular data via DataTables
  • rthreejs -- Interactive 3D graphics

The package was created in collaboration by Ramnath Vaidyanathan, Joe Cheng, JJ Allaire, Yihui Xie, and Kenton Russell. We've all spent countless hours building bindings between R and the web, and were motivated to create a framework that made this as easy as possible for all R developers.

Getting Started

If you know R and a bit of JavaScript it's very straightforward to create your own widgets. You can install the htmlwidgets package from CRAN:

install.packages("htmlwidgets")

You can alternatively install the development version of htmlwidgets from GitHub as follows:

devtools::install_github('ramnathv/htmlwidgets')

There are several articles on the htmlwidgets website that will help you get you off the ground quickly and take advantage of all of the capabilities of the framework:

htmlwidgets's People

Contributors

andrewdenner avatar asifm avatar atusy avatar bborgesr avatar bwlewis avatar chk1 avatar christophergandrud avatar cpsievert avatar ekstroem avatar gaborcsardi avatar gadenbuie avatar hafen avatar jankurianski avatar jcheng5 avatar jjallaire avatar kbroman avatar kevinushey avatar liripo avatar ramnathv avatar schloerke avatar timelyportfolio avatar wch avatar yihui avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

htmlwidgets's Issues

make widgets also available as a web asset

If htmlwidgets is as successful as I think it will be, a separate package for each widget will get quite unwieldy. Maybe think about each widget as both an R package or web-available structure (gh-pages branch), so that a separate install is not required. I think htmlwidgets really is the only thing that absolutely needs to be a package.

Think about all the install_github issues we have had with rCharts and slidify and this very quickly becomes nightmarish. If we could get htmlwidgets on CRAN, and it should be simple since it is so targeted, then things become much easier if there is only one package install.

I can not reproduce the example..

Hi there.. I was able to reproduce the example rmd document before.. Now I can not..Here is the output I get. I am using RStudio version Version 0.98.1028. And the session info is given below.. I got the same error when I try with R version 3.1 also.. Am I missing something?

Thanks!!

|...................... | 33%
ordinary text without R code

|........................................... | 67%
label: unnamed-chunk-1

processing file: ttst.Rmd
|.................................................................| 100%
inline R code fragments

Quitting from lines 16-24 (ttst.Rmd)
Error in is.null(knitrOptions) : 'knitrOptions' is missing
Calls: ... -> toHTML -> toHTML.htmlwidget -> resolveSizing
Execution halted

sessionInfo()

R version 3.0.3 (2014-03-06)
Platform: x86_64-w64-mingw32/x64 (64-bit)

locale:
[1] LC_COLLATE=English_United States.1252 LC_CTYPE=English_United States.1252
[3] LC_MONETARY=English_United States.1252 LC_NUMERIC=C
[5] LC_TIME=English_United States.1252

attached base packages:
[1] stats graphics grDevices utils datasets methods base

other attached packages:
[1] knob_0.2 htmlwidgets_0.2.2 htmltools_0.2.6 sparkline_1.0

loaded via a namespace (and not attached):
[1] devtools_1.5 digest_0.6.4 evaluate_0.5.5 formatR_0.10 httr_0.4
[6] knitr_1.6 memoise_0.2.1 parallel_3.0.3 RCurl_1.95-4.3 RJSONIO_1.3-0
[11] rmarkdown_0.2.64 stringr_0.6.2 tools_3.0.3 whisker_0.3-2 yaml_2.1.13

Wish List/ Under Development

The idea of building htmlwidgets seems to be taking off and although the idea is that they are pretty easy to develop, this may be beyond some of us

timelyportfolio seems to be receiving some suggestions at buildingwidgets and there have also been several ideas via twitter. I'd be interested in the timeline aspects of [vis.js}(http://visjs.org/)

Is there a way you could set up a catalog of projects perhaps including finished, under development and suggested so that people could get ideas of what could be worked or might be available in the forseeable future

consider a javascript structure to house our widgets

In the case of multiple widgets, there will be multiple payload which could be confusing and also possibly cause conflicts. In addition a widget often will return an object that allows us to do things with it beyond the initial draw/build. As an example, I have changed getScript in my fork to something like this:

getScript <- function(x, file, package){
  payload = sprintf("var payload = %s\n", RJSONIO::toJSON(x))
  # simple check for url; probably more robust method out there
  if( grep("^http",file) ) {
    script_file <- file
  } else script_file <- system.file(file, package = package)
  lines <- readLines(script_file, warn = FALSE, encoding = "UTF-8")
  tags$script(
    HTML(
      sprintf(
"
var htmlWidgets = typeof(htmlWidgets) == 'undefined' ? [] : htmlWidgets;
htmlWidgets.push(
  (function(){
    %s
    %s
  })()
)
"
, paste0(payload,collapse="n")
, paste0(lines,collapse="\n")
      )
    )
  )
}

and then a {{widget}}.js that does something like this:

return {
  "widget" : new Spinner(payload.config).spin(document.getElementById(payload.id)),
  "payload" : payload,
  "type" : "spin"
};

To test it for yourself, you could

devtools::install_github("timelyportfolio/htmlwidgets")
source('http://timelyportfolio.github.io/htmlwidgets_spin/R/spin.R')
spin()

Would love thoughts.

dynamically adjust height and width of an htmlwidget in Shiny

rich-iannone/DiagrammeR#93 brings up an interesting point that I have not considered before. What is the best way to handle this, since initialize and resize are not called in this scenario? I can only think to provide height and width also in x so that the adjustment can be made in renderValue.

Using renderPlot as an example, should we add height and width as arguments to the template render* for htmlwidgets?

Allow access to object returned by renderValue

The basic idea here (thanks @timelyportfolio) is to provide users a mechanism to access the return value of renderValue so that users can inject additional behavior using javascript. Since all instances of a widget have a unique id and this is accessible to renderValue, I believe we can use the id as key to identify the widget.

The easiest way to do this probably is to create a HTMLWidgets.obj object and push the output of renderValue with the widget id as key.

@jcheng5 @jjallaire Your thoughts on this? If the implementation is easy, I think it makes sense to do this as it will open up a wider range of use cases.

styling and other attributes for widgets

tags in htmltools have the very nice feature of being able to append attributes in the constructor or through tagAppendAttributes. As of now, if we wanted to pass something like a style attribute to our widget, there is no built-in mechanism like there is with tags to allow this. style is even more difficult since these lines in toHTML override anything that could be passed with just a width and height.

For the sake of an example, let's say we want to make all our knob widgets style="float:left;". I think we should be able to do something like

k1 <- knob(value = 20, min = 0, max = 100, 
           angleArc = 250, angleOffset = -125, 
           fgColor = "#66CC66",height = 200, width =200)
html_print(tagList(
    lapply(1:4,function(x){return(k1)})
   ,
   # this could work but see discussion below
   lapply(1:4,function(x){return(tags$div(k1,style="float:left;"))})
))

I know this is not the perfect example since we could do tags$div(knob(),style="float:left;") or use css style, but style is most interesting since it is overwritten. Also, the widget author could provide a mechanism to apply these in Javascript, but that is not as clean as I would like, and if the audience is mainly R developers making shiny apps could cause problems.

Just something to think about and consider.

Feeding the proper json-shaped data to d3pie

I am trying to use htmlwidgets to create a simple package using the d3 library d3pie.
Frankly I've got much ahead much faster than what I would have expected: well done guys.

On the other hand while I manage to get the title & subtitle to show, now I'm stuck with finding the correct json data shape for the d3pie library.
I've tried numerous variants but clearly I'm not succeeding.
This is the error I'm currently getting:

not valid:  Object
label: "JavaScript"
value: "264131"
__proto__: Object

I need some help!
The package is here: https://github.com/smartinsightsfromdata/rd3pie
(please note I've not even started to think about how to render the pie in shiny, even if it is my final goal)

get the result of a "click" back to the server

I'm working to a wrapper for the great library datamaps (already available with rCharts but with issues with shiny, so I thought of a brand new implementation).

At present everything seems to work. Only one important functionality is missing.

I would like to get back to the server the name of the state (or postcode, county etc.) that the user has clicked. This callback is already available with datamaps:

done : function(datamap) {
  datamap.svg.selectAll('.datamaps-subunit').on('click', function(geography) {
    alert(geography.properties.name);
  });
  },

And my code duly displays (useless!) popups with the name of the states you click.

How to get geography.properties.name back to the R server??

(apologies but I am NO javascript programmer!)

allow pandoc option setting from saveWidget

When trying to prep some examples for a blog post for a new widget I tripped the following error:

Stack space overflow: current size 33632 bytes.
Use `+RTS -Ksize -RTS' to increase it.
Error: pandoc document conversion failed with error 2

It doesn't happen when knitting, though, so this may just be a weird edge case.

library(dplyr)
library(babynames)
library(streamgraph) # devtools::install_github("hrbrmstr/streamgraph")

babynames %>%
  filter(grepl("^Kr", name)) %>%
  group_by(year, name) %>%
  tally(wt=n) %>%
  streamgraph("name", "n", "year", offset="zero", interpolate="linear", width=750, height=310) %>%
  sg_legend(show=TRUE, label="Kr- names: ") -> two

saveWidget(two, "two.html", TRUE)

to reproduce (but a restart of RStudio also cleared the error at least one time for me).

UPDATE

It's pretty reproducible, but even using the extra parameters from the command line (bash, pandoc 1.13.x, os x 10.10.x) didn't solve the issue. I think it requires compiling pandoc to support rts options. ugh.

folks might just want to be careful when making ginormous vis.

slidify and DiagrammeR

Hi all,

I'm trying to get slidify to work together with a diagram created by DiagrammeR. I read in one of the issues here that slidify and htmlwidgets don't play nicely together yet, but there were some suggestions to get things up and running by saving the widget as html and then including that in iframe.

I've included my example below. My "current" problem is that when including the widget in the iframe the background becomes white. Can I change that in some way so that it blends into the general format for my slides (i.e., have my background as transparent)?


---
title       : Test
subtitle    : 
author      : Claus
job         : 
framework   : revealjs      # {io2012, html5slides, shower, dzslides, ...}
highlighter : highlight.js  # {highlight.js, prettify, highlight}
hitheme     : zenburn       # 
widgets     : [mathjax angularjs]     # {mathjax, quiz, bootstrap}
mode        : standalone   # {selfcontained, standalone, draft}
knit        : slidify::knit2slides
revealjs    :
  theme: serif 
  incremental: true

---

## Read-And-Delete

1. Edit YAML front matter
2. Write using R Markdown
3. Use an empty line followed by three dashes to separate slides!

--- .class #id 


<style>
iframe {
  width: 800px;
  height: 600px;
  background: #AA0000;
}
</style>
<style>iframe.nvd3{height: 600px;width: 800px}</style>

## Slide 2

   ```{r,echo=FALSE,results=FALSE,warning=FALSE}
   library(htmlwidgets)
   library(DiagrammeR)
    ```

   ```{r echo=FALSE, warning=FALSE,cache=FALSE,results='asis'}
fff <- DiagrammeR::grViz("
  digraph neato {


    rankdir=LR;
    node[width=1,height=1,fixedsize=true];

    { rank=same; B ; C}

    edge[weight=20];
    A -> B [label='&beta;'];
    B -> O [label='&gamma;'];
    edge[weight=1];

    C -> { B O }

    A -> O[label='&beta;&gamma;'];

    A[label='Assigned\ntreatment',shape=circle,fillcolor='lightblue',style='filled'];
    B[label='Actual\ntreatment',shape=circle,fillcolor='lightblue',style='filled'];
    C[label='Confounder',shape=circle,fillcolor='MistyRose',style='filled'];
    O[label='Outcome',shape=circle,fillcolor='lightblue',style='filled'];


  }
", width=650, height=400, engine="dot")

saveWidget(fff, 'diagram.html')
cat('<iframe src="diagram.html" width=100% height=100% allowtransparency="true" style="background: #FFCCFF;"> </iframe>')

iframe support

It would be nice to have an iframe option for embedding an htmlwidget to deal with the inevitable case of conflicts between the libraries required by the web page containing the widget and the libraries in the widget itself.

the x parameter in the function renderValue is always undefined

Dear all,

I'm using the current version of shiny and htmlwidgets, with R 3.1.3 and RStudio 0.99.386 under OSX. Do you see any reason why my x parameter is always undefined in my own widget. In the following example x$svg is the content of an SVG file. Thanks for your help!!

file cola.R

cola <- function(fileName, width = NULL, height = NULL) {

  # forward options using x
  x<- list()
  x$svg <- paste(readLines(fileName), collapse="")

  # create widget
  htmlwidgets::createWidget(name = 'cola', x, width = width, height = height, package = 'cola')
}

file cola.js

HTMLWidgets.widget({

  name: 'cola',

  type: 'output',

  initialize: function(el, width, height) {

  //create a vis object with the library d3.js  
    return {
      vis : vis
    }

  },

  renderValue: function(el, x, instance) {
    console.log(el); //OK!
    console.log(x); //always undefined according to the javascript console
    console.log(instance); //OK!
  },

  resize: function(el, width, height, instance) {

  }

});

file server.R

output$test <- renderCola({
    cola(system.file("htmlwidgets/examples/sucrose.svg", package = "cola"))
  })

RThreeJS example doesn't work

earth <- texture(system.file("htmlwidgets/lib/globe/world.jpg",package="threejs"))
No such file or directory
Error in encode(data, tf) : b64:002:File Error Opening/Creating Files.

saveWidget self contained encoding issue

I am having an issue saving an htmlwidget (self contained).

devtools::install_github("jpmarindiaz/d3plus")
library(d3plus)
countries <- read.csv(system.file("data/countries.csv", package = "d3plus"))
s <- d3plus("tree", countries)
htmlwidgets::saveWidget(s,"index.html")

Looking at the console, d3 cannot be loaded because there are some encoding issues:
Look at the special characters in the d3.js v3.5.5 file:
var ε = 1e-6, ε2 = ε * ε, π = Math.PI, τ = 2 * π, τε = τ - ε, halfπ = π / 2, d3_radians = π / 180, d3_degrees = 180 / π; https://github.com/jpmarindiaz/d3plus/blob/master/inst/htmlwidgets/lib/d3-v3.5.5/d3.js#L1261

They get encoded into this (when the self contained = TRUE)
var ε = 1e-6, ε2 = ε * ε, π = Math.PI, τ = 2 * π, τε = τ - ε, halfπ = π / 2, d3_radians = π / 180, d3_degrees = 180 / π;

Thanks for your help!

Making widgets talk to each other!

I have been experimenting with the ability to use widgets as components and then allow them to communicate with each other.

Here are some examples

  1. Life Expectancy Choropleth + Line
  2. Datamaps + Chroniton
  3. Datamaps + Morris + Sortable

The first example uses shiny, while the second does not.

In the case of Shiny, I am using Shiny.onInputChange to trigger changes and communicate back to the server from the client on mouseover. In the second example, I am directly manipulating the datamap from the callback code of the chroniton widget.

The more I think about this, I believe that something like a pubsub design pattern will be helpful here. htmlwidgets can provide basic pubsub infrastructure and widget authors can provide callbacks that allow users to broadcast data on certain events. In a similar vein, widget authors can also expose callbacks that allow a widget to listen to certain events and trigger a change.

By following some conventions regarding how these events are named, it would be possible for widgets to talk to each other using pubsub. Currently, this idea is still very hazy in my mind, but I wanted to put it out here so that it can trigger a broader discussion.

In the shiny case, things are easier, since Shiny.onInputChange provides a simple pubsub mechanism. I am thinking of a more general mechanism that will work with and without Shiny.

@jcheng5 I would really appreciate any thoughts you have on this, given your experience working with javascript libraries. @jjallaire @timelyportfolio @yihui Any thoughts/comments on this idea would be welcome, as I think it would be a very powerful way forward for htmlwidgets to create a componentized architecture.

Is this OK for input / output widget as well?

I use handsontable. There are various implementations (including one I've sort of cobbled together from different authors), but it is not implemented properly. As an example proper sizing is not available.

On the other hand it seems that htmlwidgets is mainly designed for output, while one of the advantages of handsontable is that you can also read what a user has written into the table.

Should I attempt to port the code I have running into the htmlwidgets scaffolding?

Would it be worth it?

(but then what about things like < type: 'output' > in the js file?)

More examples / tutorials please...

Hi,

Thanks for the good work and this package looks really interesting. However as a intermediate R user I'd like to know more about the following topics (under Shiny context) through example codes on the website or Rmd file:

  1. How to save a chart (networkD3) on Shiny app. I tried to use saveWidget and downloadHandler, but didn't work.
  2. How to bind a mouse click to shiny reactive value to enable filtering or etc.
  3. What's the R version of following code?
    $('.bar').sparkline(values, {
    type: 'bar',
    tooltipFormat: '{{value:levels}} - {{value}}',
    tooltipValueLookups: {
    levels: $.range_map({ ':2': 'Low', '3:6': 'Medium', '7:': 'High' })
    }
    });

Thanks...

transposeArray2D example?

I understand (and I've successfully used in one of my wrapper packages) the dataframeToD3 transformation.
The transformation I need is a bit more complex, as it is based on a "GROUP BY" (I can do it in R and with one of the JSON packages of course, but I'm bound to follow your design).

Is this the use case for transposeArray2D? A simple example would be great.
For example, I need to produce a JSON like the one below, with a "GROUP BY" country.

AUS: {
            fill: 'Overdue',
            Milestones: 100,
            Tasks: 487
        },
USA: {
            fill: 'Active',
            Tasks:32
        }

Automatically generating dependencies YAML from a bower install

Bower makes it really easy to download a javascript library and its associated dependencies. Moreover, the bower.json files contains useful information for specifying the dependency. We can write a function that will allow widget package authors to automatically create the YAML from the bower.json files. Let me illustrate with an example.

#' Construct YAML spec from a bower.json file
bower_to_yaml <- function(dep){
  bower = RJSONIO::fromJSON(file.path(dep, 'bower.json'))
  spec = list(
    name = bower$name, 
    version = bower$version,
    src = paste0('htmlwidgets/', dep),
    script = bower$main[grepl('^.*\\.js$', bower$main)],
    style = bower$main[grepl('^.*\\.css$', bower$main)]
  )
  return(Filter(function(x) length(x) != 0, spec))
}

#' Construct dependency YAML from path to `bower_components`.
create_config_yaml <- function(path){
  deps <- dir(path, full = TRUE)
  yaml::as.yaml(list(dependencies = lapply(deps, bower_to_yaml)))
}

To test it out, we can install morris.js using bower and then running create_config_yaml to output the YAML dependency. In this particular case, we would need to manually tweak the YAML to remove mocha.js which is not really required for production.

system('bower install morris.js')
create_config_yaml('bower_components')

The key question is whether this should be included along with htmlwidgets as a utility function to help package authors. @jcheng5 , @jjallaire any thoughts around this?

repeated creation of objects in shiny

My d3pie wrapper, rd3pie works fine. On the other hand, surprisingly during testing we found that in a reactive function in shiniy (e.g. a sliderInput) multiple pies were created!

We eliminated the issue with the following code:

  if (el.hasChildNodes()) {
//  alert(el.hasChildNodes());
    while (el.firstChild) {
      el.removeChild(el.firstChild);
    }
  }

See the entire code here.

A demo is available here. Take into account that the problem does not occur anymore because of the above fix.

Now I'm encountering a similar issue with datamaps.

Is this an expected behaviour?

Is there a better way to address the problem than the one above?

As I've already encountered this 2 times, perhaps it should be covered in one of the vignettes?

Thanks for these great packages!!

export htmlwidgets (SVG) as PNG

It seems a very common request is the ability to export the SVG rendered from an htmlwidget as a static graphic PNG. This https://github.com/timelyportfolio/exportwidget will be the widget of the week. I hope it will be a good experiment to start the conversation how best to make this available to htmlwidget authors. There are a couple examples in the Readme.

Currently, exportwidget uses canvg (not necessary but very handy) to draw the SVG to a canvas element. Then with toDataURL it makes a PNG to download. I add the function pngify to window.HTMLWidgets, so that in theory it could be available as a helper for widget authors who might want to add it as a post-render task. I am aware of similar functionality in ggvis in these lines.

I consider it pretty ugly at this point, but with some iteration I think it could be a valuable addition to the htmlwidget infrastructure. Please let me know if you have thoughts. Thanks.

Non-ascii yaml 'attachments' don't work with rmarkdown/pandoc

This is not a bug, just something to note (that maybe should eventually be documented somewhere).

The yaml dependency syntax technically supports including any kind of file in the widget; including things like supporting image files can sometimes be useful. But that flexibility is not compatible with pandoc/rmarkdown. For example, including jpeg files specified as yaml attachments like this (from a version of the threejs package):

dependencies:
  - name: d3
    version: 3.4.11
    src: "htmlwidgets/lib/d3-3.4.11"
    script: d3.v3.min.js
  - name: threejs
    version: 69
    src: "htmlwidgets/lib/threejs-69"
    script:
      - three.min.js
      - Detector.js
      - Projector.js
      - CanvasRenderer.js
  - name: globe
    version: 1
    src: "htmlwidgets/lib/globe"
    attachment:
      - world.jpg
      - moon.jpg
      - mars.jpg
      - jupiter.jpg

ends up producing errors in rmarkdown like this:

pandoc: Cannot decode byte '\xff': Data.Text.Encoding.Fusion.streamUtf8: Invalid UTF-8 stream
Error: pandoc document conversion failed with error 1

See the threejs bug here bwlewis/rthreejs#5

My advice is to not include binary files like images in yaml-specified dependencies, even though this is technically possible. Use dataURI encoding instead to supply images to your documents.

Data containing date format characters is inconsistently converted from R to JS at different nest levels

I did something like this:

data <- data.frame(date = c("2015-01-01", "2015-01-02"), value = 1:2)
x <- list(data = data, nested_data = list(data = data) )
createWidget(hoge, x)

Then, I got the result below. x.nested_data.data is automatically converted to some strange form, while x.data is converted as I expected.

> JSON.stringify(x.data)
"{"date":["2015-01-01","2015-01-02"],"value":[1,2]}"
> JSON.stringify(x.nested_data.data)
"[[{"date":"2014-12-31T15:00:00.000Z","value":1,"line_id":1},{"date":"2015-01-01T15:00:00.000Z","value":2,"line_id":1}]]"

Is this a bug or a limitation? (Sorry if I overlooked some notices on the document)

Tagging releases

Can you tag release(s) (at the very least the latest release)? It will make it easier to build a conda package, especially since the latest version on CRAN doesn't work with rbokeh.

how to handle callback / post-render behaviors ?

Do we expect htmlwidgets to handle callback or other post-render behaviors, or should we expect widget authors to handle on their end? dygraphs handles very nicely. I wonder though given potential lack of Javascript knowledge on the part of some widget authors (like me :)) if we can establish some best practices for this. See rich-iannone/DiagrammeR#10.

issue with RJSONIO

I've just found a problem in the conversion(s) between data.table and RJSONIO.
This is affecting the library I developed: rpivotTable here
I have documented the problem with reproducible code here and reported it to the data.table project.
I think it strengthen the case for htmlwidgets conversion to adopting jsonlite (which does not appear to have the issue).

call {{widget}}.js something else to prevent confusion

In the first set of very nice examples, the convention seems to be to name the little .js widget script {{widget}}.js. This might cause confusion since the actually js dependency will also likely be called the same thing. Should the convention be changed slightly to something like widget_{{widget}}.js?

D3.js(not minified) contains corrupted characters after rmarkdown::render()

D3.js(not minified) contains corrupted characters after rmarkdown::render()

Disclaimer: I don't mean to blame htmlwidgets for this problem, this seems the problem of rmarkdown.

I'm trying to create a widget with metrics-graphics.js. The repo is here. I can successfully render charts in RStudio's view, but I cannot see those charts in knitted Rmarkdown files.

The cause of this seems that D3.js (non-minified version) contains non-ASCII characters and those are converted to corrupted characters.

Package detail

Before describing the problem, I write about the way I created this package.

I executed the following code; metric-graphics.js, d3.js and jquery are deployed into inst/htmlwidgets/lib automatically.

htmlwidgets::scaffoldWidget("metricsgraphics", bowerPkg = "metrics-graphics")

I edited R and Javascript a bit.

Code to draw chart

Here is my code to generate a chart with metricsgraphics. I could successfully draw a chart in RStudio's view.

devtools::install_github("yutannihilation/metricsgraphics")

library(metricsgraphics)
library(wikipediatrend)
library(dplyr)

wp_trend() %>%
  transmute(date = as.character(date), value = count) %>%
  metricsgraphics

But when I knit the Rmarkdown file that contains the code above, I couldn't see the chart.

Rmarkdown: https://gist.github.com/yutannihilation/359f3ac06b3cc3cd9a62#file-test-rmd
knitted HTML: https://gist.github.com/yutannihilation/359f3ac06b3cc3cd9a62#file-test-html

Problem details

I found this error in my browser's console.

console_d3js

The line 1225 has these invalid characters:

knitted_d3js

The same line in RStudio's view (which displays chart correctly) is this:

rstudio_d3js

I think those widgets that uses minified version of D3.js, such as networkD3, won't suffer from this problem. But I'm afraid everyone who tries to create widgets using D3.js by bowerPkg option will suffer this problem.

If we can specify Bower options like --minify, we may avoid this problem. (c.f. Install option --minified · Issue #368 · bower/bower) But I'm not sure. Umm...

Do you have some good idea? Sorry for this long post...

(Later, I will report this issue on rstudio/rmarkdown also.)

Stable item IDs

Autogenerated item IDs seem to be random numbers (code reference). When a widget is embedded in an HTML document, the ID is reassigned every time. Even if the random seed is reset in the beginning of the document, unrelated changes can lead to a change in the widget IDs for the entire document.

How about using a hash of the data instead:

id <- paste("htmlwidget", digest::digest(x), sep="-")

Slightly related: rstudio/DT#72

d3 + React.js

(Sorry, I know this is a long message. I'm happy to discuss/demo over vchat or in person at the rOpenSci event in late March.)

Motivation

d3 is awesome, we all know that.

I've always felt d3 presented a programming model that was subtle and tricky enough, that it might be irresponsible to ask casual JavaScript programmers to make sense of it. It's fairly easy to start from a Bostock example and get an 80% correct solution (doesn't work correctly in Shiny, doesn't resize properly) but the other 20% will require a deep understanding of d3. And there's not enough commonality between different pure d3 widgets for htmlwidgets (or Shiny, for that matter) to provide much leverage to widget developers.

However, d3 + React together feels to me like it just might be simple enough to enable casual JS developers to succeed in making 100% correct widgets. I'm not saying it'll be easy for them, but it doesn't feel unreasonable to me anymore.

And even for expert widget authors, d3 + React just requires less thought and eliminates entire classes of bugs. It's faster to write, simpler to read, easier to reason about.

How React can be used with d3

You can think of d3 as 1) a set of "abstract" implementations of projections, layouts, scales, parsers, etc., and by abstract I mean not tied to any particular visual representation; 2) a data-to-DOM-node data binding mechanism (selectAll/data/enter/exit); and 3) a set of functions for mutating DOM nodes (attr, append).

My assertion is that React is a perfect replacement for 2 and 3, and offers significant advantages over using d3 alone. The enter/exit mechanism is beautiful and elegant, but quite difficult to grasp (it took me maybe 3 tries over a period of months to really grok it) and in the context of htmlwidgets, very easy to get wrong. If you put too much functionality in the enter() side of things, your widget will look great in static contexts like RStudio viewer or Rmd, but will be broken in Shiny apps. Unfortunately, many if not most of the d3 examples in the wild are written in this style.

d3's DOM mutation API is straightforward but it doesn't lead to particularly readable code. It's not easy to look at d3 code and intuit the DOM tree that's being built up.

React solves both of these problems by working in a far more, well, reactive way. Your renderValue callback just needs to create a React virtual DOM and render it. For the most part you don't need to worry about what state is already in the DOM and how to get it to the state that you want; just create the DOM tree you want, and let React figure out how to render it.

In other words, both d3 and d3+React are straightforward for rendering a widget in "State A". What happens now when you want to transition the widget to "State B"? In d3, you code up the instructions for 1) creating new nodes, 2) removing no-longer-needed nodes, 3) changing existing nodes. In d3 + React, you essentially just write the desired HTML you want to see at the end, and let React take care of turning that into the same set of instructions d3 would execute.

Example

Compare these two rendering codepaths:

The React version was much faster to write, much easier to read, and definitely has "good enough" performance (I didn't measure the pure d3 implementation but IIRC the React version can render 10,000 bubbles in a second).

It's not a perfectly fair comparison right now because the React example doesn't do smoothly animated transitions, I haven't learned how to do those in React just yet but I'm assuming/hoping there's a nice React mechanism and it will work sensibly in htmlwidgets.

Next steps

I'm creating this issue to foster discussion around this approach (would especially like feedback from @timelyportfolio). I would love others who are trying to build d3-based htmlwidgets to try swapping in React for the actual rendering. See https://github.com/jcheng5/bubbles/tree/react for an example. I would especially love to know whether transitions can be achieved easily.

If it turns out that this is as useful a combination as I hope it is, then I would like to discuss how we could make it easier for potential widget authors to work with it. At least a tutorial somewhere (either at htmlwidgets.org or Kenton's blog), all the way up to having a facade over HTMLWidgets.widget() that is specific for d3 + React.

Build notes

The biggest downside of the way I wrote the React version of the bubbles package is that it uses JSX, which lets you put "HTML" right inline in your JavaScript; it's cool but requires a build-time compile step. So you need to install the JSX compiler (install Node.js, then run sudo npm install -g react-tools). If you're on Mac or Linux you can use the build.sh script in the root of the package; either run it without arguments, or pass it -w to have it continuously monitor the JSX file and build the resulting JS. If you choose to use JSX in your package, please check in the generated JavaScript so people can install your package using devtools::install_github and won't need the JSX compiler to be installed.

If you want you can skip JSX and just write straight JavaScript, here's what that looks like. (It's possible for it to be a bit more terse using React.DOM, but it's not a gigantic difference.) Definitely doesn't look as nice but the semantics are identical.

Validating JavaScript

The JS function could use the V8 package to compose or validate JavaScript code. The only thing to look out for is that V8 does not allow for defining anonymous objects or functions in the global scope, so you need to assign them to something. For example:

jscode = '{
  name: "sigma",
  type: "output",
  initialize: function(el, width, height) {
    // create our sigma object and bind it to the element
    var sig = new sigma(el.id);
    // return it as part of our instance data
    return {
      sig: sig
    };
  },
  renderValue: function(el, x, instance) {
    // parse gexf data
    var parser = new DOMParser();
    var data = parser.parseFromString(x.data, "application/xml");

    // apply settings
    for (var name in x.settings)
      instance.sig.settings(name, x.settings[name]);

    // update the sigma instance
    sigma.parsers.gexf(
      data,          // parsed gexf data
      instance.sig,  // sigma instance we created in initialize
      function() {
        // need to call refresh to reflect new settings and data
        instance.sig.refresh();
      }
    );
  },

  resize: function(el, width, height, instance) {

    // forward resize on to sigma renderers
    for (var name in instance.sig.renderers)
      instance.sig.renderers[name].resize(width, height);  
  }
}'

And then:

ct <- new_context()
ct$validate(paste("output = ", jscode))

set up google group or some other discussion channel

Should we set up a Google group or some other discussion channel to prevent duplication of work on js libraries? Fortunately, there are already quite a few new widgets in active development, and it seems many of the authors have quite a few more planned. It would be helpful I think to have a central place where widget authors can discuss active widgets and plans for widgets, so that those mutually interested in a js library can collaborate instead of duplicate.

findBower() returns NULL

I don't install Bower yet, so scaffoldWidget() should stop here:

 if (findBower() == ""){

But findBower() returns NULL, accordingly this if-statement fails.

I think else is needed here, instead of here (I'm using Ubuntu 14.04)

jQuery check false but then uses `.filter` from jQuery

In a Shiny context, these lines call lines in which there is a check for jQuery

    if (typeof(jQuery) !== "undefined" && scope instanceof jQuery) {
      return scope.find(selector);
    }
    if (scope.querySelectorAll) {
      return scope.querySelectorAll(selector);
    }

This makes sense, but there are two issues.

  1. if no jQuery then scope is an "array", so scope[0].querySelectorAll will need to be used
  2. on the return, this line tries to use filter from jQuery, which of course will not work if jQuery is not available.

I discovered this because rbokeh bokeh/rbokeh#25 uses jQuery, so the jQuery returns false even though it is really there. This is another issue that I'm trying to address.

Widgets repository

It might be nice if htmlwidgets would somehow support (and encourages to use) a dedicated widgets package repository (or some other module manager) to prevent CRAN from being flooded with countless packages wrapping around a contemporary JS libraries.

Maybe something as simple as install_widget that wraps around devtools::install_github to a particular github organisation would do.

Print methods for widgets and rendering in R Markdown

I have a widget where I build and update an object through various commands and then it is not until the object is printed that createWidget() is called. I have good reasons for this and it will probably be a common use case. This results in behavior where if I am in an interactive console and I print my object, the widget is rendered in the browser, but if I embed the same code in an R Markdown document, the widget is not embedded in the resulting document.

As an example, take the widget you get from scaffoldWidget() but modify mywidget.R to have the following:

mywidget <- function(message, width = NULL, height = NULL) {
  structure(list(message = message, width = width, height = height), 
    class = "mywidgetobj")
}

print.mywidgetobj <- function(x, ...) {
  w <- htmlwidgets::createWidget(
    name = 'mywidget',
    list(message = x$message),
    width = x$width,
    height = x$height,
    package = 'mywidget'
  )
  print(w)
}

Now the following will give me back the widget when run in an interactive console:

a <- mywidget("hello world")
a

But in an R Markdown document, it also opens the widget in the browser when it should instead be embedding it in the document.

switch off html escaping

Hi,

I need to produce some links in a D3TableFilter widget, but HTML seems to arrive escaped on the JavaScript side. I there a way to avoid this, or do I have to un-escape using JavaScript?

Thanks

Thomas

rmarkdown with include FALSE

Hi

Before I upgraded to rmarkdown package version 0.6.1. I could simply use include = FALSE in the R chunks and I would get the htmlwidget rendered without the chunk code as expected.
Now, when I use include = FALSE in the chunks, the htmlwidgets do not get rendered at all.

Here is an example from the networkD3 htmlwidget:


---
title: "R Markdown networkD3"
output: 
  html_document:
    toc: true

---

## simpleNetwork

```{r, include=FALSE}
library(networkD3)
src <- c("A", "A", "A", "A", "B", "B", "C", "C", "D")
target <- c("B", "C", "D", "J", "E", "F", "G", "H", "I")
networkData <- data.frame(src, target)
simpleNetwork(networkData)
```
R version 3.1.3 (2015-03-09)
Platform: x86_64-apple-darwin13.4.0 (64-bit)
Running under: OS X 10.10.2 (Yosemite)

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] htmlwidgets_0.3.3

loaded via a namespace (and not attached):
[1] digest_0.6.8     htmltools_0.2.6  knitr_1.10.5     rmarkdown_0.6.1  rsconnect_0.3.78
[6] tools_3.1.3      yaml_2.1.13  

Correct idiom to inject static JSON Data

I'm attempting to "simply" draw a map using D3 and D3 Projections via htmlwidgets. My question is how to "inject" the shapes data, in the "htmlwidgets" way.

Currently, my renderValue looks like:

  renderValue: function(el, x, instance) {

    var path = d3.geo.path()
    .projection(instance.projection);

    // select the svg element and remove existing childern
    var svg = d3.select(el).select("svg");
    svg.selectAll("*").remove();

    d3.json("htmlwidgets/lib/shapes/world-110m.json", function(error, world) {
      svg.insert("path", ".trip")
          .datum(topojson.feature(world, world.objects.land))
          .attr("class", "land")
          .attr("d", path);

      svg.insert("path", ".trip")
          .datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
          .attr("class", "boundary")
          .attr("d", path);
  });
},

My question centers on this line:

d3.json("htmlwidgets/lib/shapes/world-110m.json", function(error, world) {

What is the idiomatic way to, in general, provide static data like this?

My yaml looks like:

dependencies:
- name: d3-geo-projection
  version: 0.2.12
  src: htmlwidgets/lib/d3-geo-projection
  script: d3-geo-projection.js
- name: d3
  version: 3.5.3
  src: htmlwidgets/lib/d3
  script: d3.min.js
- name: topojson
  version: 1.6.18
  src: htmlwidgets/lib/topojson
  script: topojson.js

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.