block by timelyportfolio 55f054d1e2a47b8942d155ae05c4e84e

vue and networkD3 in R

Full Screen

combining htmlwidgets and Vue.js

As I work more with Vue.js, I am impressed and delighted. See this article comparing Vue and React well if you want a little more detail.

In this example, I wanted to try combining the element tree component with a networkD3 diagonalNetwork tree. Very simple, when you check the box the node circle fills with gray.

Please do not consider this best practice since I am still learning Vue.

code

library(htmltools)
library(vueR)

rhd <- treemap::random.hierarchical.data()
tl_tree <- tagList(
  tags$div(
    id = "app",
    tag(
      "el-tree",
      list(
        "ref" = "mytree",
        ":data" = "data.children",
        ":props" = "defaultProps",
        "show-checkbox" = NA,
        "@check-change" = "handleCheckChange"
      )
    ),
    tags$pre("{{checkedNodes.map(function(d){return d.name})}}")
  ),
  vue(
    list(
      el="#app",
      data = list(
        data = d3r::d3_nest(
          rhd,
          value_cols="x"
        ),
        defaultProps = list(
          'children' = 'children',
          'label' = 'name'
        ),
        checkedNodes = list()
      ),
      methods = list(
        handleCheckChange = htmlwidgets::JS(
          "
function(data, checked, indeterminate){
  //debugger;
  //data.checked = checked && !indeterminate;
  console.log(data, checked, indeterminate);
  this.checkedNodes = this.$refs.mytree.getCheckedNodes();
  this.$emit('tree-checked');
}
          "
        )
      )
    ),
    minified = FALSE
  )
)

browsable(
  attachDependencies(
    tl_tree,
    list(
      element
    )
  )
)


# add a d3 tree and make it respond to Vue tree
d3t <- networkD3::diagonalNetwork(
  jsonlite::fromJSON(
    d3r::d3_nest(
      rhd,
      value_cols="x"
    ),
    simplifyVector=FALSE,
    simplifyDataFrame=FALSE
  )
)
# use onRender to add event handling
d3t <- htmlwidgets::onRender(
  d3t,
"
function(el, x) {
  var vue_tree = HTMLWidgets.find('.vue').instance;
  vue_tree.$on(
    'tree-checked',
    function() {
      var myvue = this;
      var checkedNodes = myvue.checkedNodes.map(function(d){return d.name});
      var nodes = d3.select(el).selectAll('g.node');
      nodes.each(function(d) {
        if(checkedNodes.indexOf(d.data.name) >= 0){
          d3.select(this).select('circle').style('fill', 'gray');
        } else {
          d3.select(this).select('circle').style('fill', 'rgb(255,255,255)');
        };
      })
    }
  )
}
"
)
browsable(
  attachDependencies(
    tagList(
      tags$div(tl_tree,style="width:20%; float:left; display:block;"),
      tags$div(d3t,style="width:70%; float:left; display:block;")
    ),
    list(
      element
    )
  )
)