闪亮的自定义输出不渲染

Jar*_*edL 2 r d3.js shiny

我正在尝试将网络可视化从D3.js绑定到Shiny中的自定义输出.出于某种原因,似乎我的渲染函数没有被调用.这是我的代码:

rbindings.js

var forceNetworkOB = new Shiny.OutputBinding();

forceNetworkOB.find = function(scope) {
  return $(scope).find("svg.rio-force-network");
};

forceNetworkOB.renderValue = function(el, graph) {

  alert('rendering')

  //actual rendering code here...

};

Shiny.outputBindings.register(forceNetworkOB, "jumpy.forceNetworkOB");
Run Code Online (Sandbox Code Playgroud)

CustomIO.R

renderForceNetwork <- function(expr, env=parent.frame(), quoted=FALSE) {

  func <- exprToFunction(expr, env, quoted)

  function() {

    # Never called
    browser()

    graph <- func()

    list(nodes = graph$nodes,
         links = graph$edges
    )
  }
}

forceNetwork <- function(id, width = '400px', height = '400px') {

  tag('svg', list(id = id, class = 'rio-force-network', width = width, height = height))

}
Run Code Online (Sandbox Code Playgroud)

ui.R

library(shiny)
source('customIO.R')

shinyUI(fluidPage(

  tags$script(src = 'js/d3.min.js'),
  tags$script(src = 'js/rbindings.js'),

  titlePanel('Network Visualization'),

  tabsetPanel(
    tabPanel('D3.js Force Layout',
      forceNetwork('vis.force', width = '800px', height = '800px'),
    )
  )

))
Run Code Online (Sandbox Code Playgroud)

和server.R

library(shiny)

source('cytoscape.R')
source('customIO.R')

shinyServer(function(session, input, output) {


  # Load the network
  network <- networkFromCytoscape('network.cyjs')

  output$vis.force <- renderForceNetwork({

    # Never called
    print('rendering')
    browser()

    list(
      nodes = data.frame(name = network$nodes.data$Label_for_display, group = rep(1, nrow(network$nodes.data))),
      edges = data.frame(from = network$edges[,1], to = network$edges[,2])
    )

  })

})
Run Code Online (Sandbox Code Playgroud)

从注释中可以看出,我的R渲染函数中的browser()行永远不会被调用,js渲染函数中的alert()也是如此.通过一些js调试,我可以看到我的自定义绑定正确地提供svg元素以及它的id.这可能很简单,但我无法弄清楚.

Xin*_*Yin 5

好吧,虽然代码似乎都是合法的,但你真的必须深入了解Shiny的源代码才能找到罪魁祸首.

当Shiny初始化时,它会调用initShiny(),然后调用bindOutputs.现在,这是bindOutputs函数的样子:

function bindOutputs(scope) {

  if (scope === undefined)
    scope = document;

  scope = $(scope);

  var bindings = outputBindings.getBindings();

  for (var i = 0; i < bindings.length; i++) {
    var binding = bindings[i].binding;
    var matches = binding.find(scope) || [];
    for (var j = 0; j < matches.length; j++) {
      var el = matches[j];
      var id = binding.getId(el);

      // Check if ID is falsy
      if (!id)
        continue;

      var bindingAdapter = new OutputBindingAdapter(el, binding);
      shinyapp.bindOutput(id, bindingAdapter);
      $(el).data('shiny-output-binding', bindingAdapter);
      $(el).addClass('shiny-bound-output'); // <- oops!
    }
  }

  // Send later in case DOM layout isn't final yet.
  setTimeout(sendImageSize, 0);
  setTimeout(sendOutputHiddenState, 0);
}
Run Code Online (Sandbox Code Playgroud)

我标记的行<- oops是导致所有问题的原因.这真的不是Shiny 本身的错误:这行代码依赖于jQuery添加一个类el,这是svg你用forceNetwork()函数创建的DOM元素.

该类shiny-bound-output对于实际绑定工作很重要.

问题是$.addClass不起作用<svg>. 有关参考,请参阅有关stackoverflow的文章此问题.

因此,您的<svg>元素缺少必需的shiny-bound-output类,可以使您的自定义OutputBinding功能正常运行.


解:

不要<svg>用作输出的容器.请<div>改用.这意味着,您应该将您的forceNetwork功能更改为:

forceNetwork <- function(id, width = '400px', height = '400px') {    
  tag('div', list(id = id, class = 'rio-force-network', width = width, height = height))
}
Run Code Online (Sandbox Code Playgroud)

您可以轻松附加<svg>使用d3.select(...).append('svg')并在那里设置宽度和高度.

(记住也要修改你的find()功能rbindings.js).


最后一句话

如果你以某种方式添加d3.select(...).append('svg')到你的javascript代码,请记住在实际d3绘图之前输出绑定的函数中clear()<div>容器renderValue().否则,每次renderForceNetwork调用时,它都会<svg><div>容器中添加一个新元素.