简体   繁体   English

带有HTML的shiny,ggvis和add_tooltip

[英]shiny, ggvis, and add_tooltip with HTML

How can I use the tags$... functions from within a ggvis interactive graphic? 如何在ggvis交互式图形中使用tags$...函数?

A "small" and contrived example: 一个“小”和人为的例子:

library(ggvis)
library(shiny)
n <- 20
data <- data.frame(
    xs = 1:n, ys = rnorm(n),
    color = sample(c('red', 'green', 'blue'), n, replace = TRUE),
    size = 25 * sample(6, n, replace = TRUE),
    rownum = 1:n)

ttFunc1 <- function(x) {
    paste('<table>',
          paste(apply(data.frame(n = names(data),
                                 x = unlist(format(data[x$rownum,]))), 1,
                      function(h) paste('<tr><td>', h[1],
                                        '</td><td>', h[2],
                                        '</td></tr>')),
                collapse = ''),
          '</table>')
}

ttFunc2 <- function(x) {
    tags$table(
        lapply(1:ncol(data),
               function(cc) {
                   tags$tr(tags$td(names(data)[cc]),
                           tags$td(format(data[x$rownum,cc])))
               }))
}

shinyApp(
    ui = fluidPage(
        uiOutput('gg_ui'),
        ggvisOutput('gg')
        ),
    server = function(input, output, session) {
        data %>%
            ggvis(~xs, ~ys, key := ~rownum) %>%
                layer_points(fill := ~color, size := ~size) %>%
                    add_tooltip(ttFunc2, 'hover') %>%
                        bind_shiny('gg', 'gg_ui')
    },
    options = list(height = 500)
)

(Not the most graceful for constructing tables, admittedly.) (诚​​然,这不是构建表格最优雅的。)

When I use ttFunc1 within the add_tooltip(...) line, the tooltip is presented correctly. 当我在add_tooltip(...)行中使用ttFunc1 ,工具提示会正确显示。 When I use the relatively-equivalent ttFunc2 , though, it's an empty tooltip. 但是,当我使用相对等效的ttFunc2时,它是一个空的工具提示。

Comparison of ttFunc1(x=list(rownum=2)) with ttFunc2(x=list(rownum=2)) shows that they are functionally equivalent. ttFunc1(x=list(rownum=2))ttFunc2(x=list(rownum=2))表明它们在功能上是等价的。

What am I missing? 我错过了什么?

The following assumes you have a recent version of Chrome with developer tools installed. 以下假设您安装了最新版本的Chrome并安装了开发人员工具。

Prelude 序幕

Let's begin with a review of the JavaScript code for ggvis -- specifically its interface with Shiny . 让我们首先回顾一下ggvis的JavaScript代码 - 特别是它与Shiny的接口

ggvis, like Shiny, communicates with the R backend through HTTP requests that are enabled by the httpuv package (originally based on the libuv C++ library). 像Shiny一样, ggvis通过httpuv包 (最初基于libuv C ++库)启用的HTTP请求与R后端进行通信。 In particular, it performs some of its communication over the Websockets protocol : R and JavaScript are constantly shuffling messages back and forth to each other using an open Websockets connection. 特别是,它通过Websockets协议执行一些通信:R和JavaScript使用开放的Websockets连接不断地来回传递消息。

Debugging with Chrome Developer Tools 使用Chrome开发者工具进行调试

In particular, after mousing over the tooltip, open the Chrome Developer console by right clicking and selecting Inspect Element. 特别是,将鼠标悬停在工具提示上后,右键单击并选择“检查元素”,打开Chrome Developer控制台。

在此输入图像描述

(If you do not see it, you may need to enable it -- Google is your friend). (如果你没有看到它,你可能需要启用它 - 谷歌是你的朋友)。 Next, bring up the Network tab, reload the page, mouse over a data point, and observe the contents with ttFunc2 after selecting the "websocket/" resource: 接下来,在选择"websocket/"资源后, ttFunc2出Network选项卡,重新加载页面,将鼠标悬停在数据点上,然后使用ttFunc2观察内容:

在此输入图像描述

You can right click and copy the contents into a file: 您可以右键单击并将内容复制到文件中:

{
   "custom": {
      "ggvis_message": {
         "type": "show_tooltip",
         "id": null,
         "data": {
            "pagex":    382,
            "pagey":    175,
            "html": {
               "name": "table",
               "attribs": [],
               "children": [
                [
                 {
                    "name": "tr",
                    ...

(I have truncated some of the contents). (我已经截断了一些内容)。 As you can notice, ggvis is receiving a message with the tooltip body, but structured as a JavaScript object. 您可以注意到,ggvis正在使用工具提示正文接收消息,但结构化为JavaScript对象。 Compare this to the ttFunc1 output: 将此与ttFunc1输出进行比较:

 {
  "custom": {
  "ggvis_message": {
  "type": "show_tooltip",
 "id": null,
 "data": {
  "pagex":    264,
 "pagey":    238,
 "html": "<table> <tr><td> xs </td><td> 7 </td></tr><tr><td> ys </td><td> -0.07295337 </td></tr><tr><td> color </td><td> red </td></tr><tr><td> size </td><td> 150 </td></tr></table>"
 }}}}

So the former request is receiving a Javascript object representing the HTML, the latter is receiving the raw HTML. 因此前一个请求是接收表示HTML的Javascript对象,后者正在接收原始HTML。 We will see momentarily why this is so. 我们将暂时看到为什么会这样。 In the meantime, notice the JavaScript code that is processing this message : 在此期间,请注意处理此消息的JavaScript代码

 // Tooltip message handlers
 ggvis.messages.addHandler("show_tooltip", function(data, id) {
   /* jshint unused: false */
   // Remove any existing tooltips
   $('.ggvis-tooltip').remove();

   // Add the tooltip div
   var $el = $('<div id="ggvis-tooltip" class="ggvis-tooltip"></div>')
     .appendTo('body');

   $el.html(data.html);
   ...

Ah hah! 啊哈! So it is using jQuery to set the HTML directly to the html element of the Websocket message. 因此,它使用jQuery将HTML直接设置为Websocket消息的html元素。 Since jQuery has never expected to interact with web-streamed output from the R htmltools package, the end result is that it receives a JavaScript object instead of a string, and the default behavior is to fail silently by displaying nothing at all. 由于jQuery从未期望与来自R htmltools包的Web流输出进行交互,因此最终结果是它接收JavaScript对象而不是字符串,并且默认行为是通过不显示任何内容而无声地失败。

On to the fix 在修复

Now that we have isolated our bug, we have a choice: we could fix this on the R side or the JavaScript side. 现在我们已经隔离了我们的bug,我们可以选择:我们可以在R端或JavaScript端修复它。 I propose the former, since transforming htmltools output should really not be the job of front-end code and violates basic developer principles like modularity. 我提出前者,因为转换htmltools输出应该不是前端代码的工作,并且违反了模块化等基本开发人员原则。

Thus, we must figure out where it is on the R side. 因此,我们必须弄清楚它在R方面的位置。 We begin by going to the ggvis github code and searching for "tooltip" (this is useful to know -- you can search through entire codebases using Github!): 我们首先转到ggvis github代码并搜索"tooltip" (知道这一点很有用 - 你可以使用Github搜索整个代码库!):

在此输入图像描述

We find interact_tooltip.R and notice the function: 我们找到interact_tooltip.R并注意函数:

show_tooltip <- function(session, l = 0, t = 0, html = "") {
  ggvis_message(session, "show_tooltip",
  list(pagex = l, pagey = t, html = html))
}

The bug is that in our example, html is a shiny.tag object rather than a character . 错误是在我们的例子中, html是一个shiny.tag对象而不是一个character Fortunately, a shiny.tag can be converted to its representing HTML using as.character as we can test from the console: 幸运的是,一个shiny.tag可以使用as.character转换为代表HTML,因为我们可以从控制台进行测试:

  > as.character(tags$table(tags$tr(tags$td('test'))))
  <table>
    <tr>
      <td>test</td>
    </tr>
  </table>

so we can go ahead and fix the code: 所以我们可以继续修复代码:

show_tooltip <- function(session, l = 0, t = 0, html = "") {
  ggvis_message(session, "show_tooltip",
  list(pagex = l, pagey = t, html = as.character(html)))
}

Helping your friends 帮助你的朋友

Now that we have found the fix, we should share it with our friends so that they can use it as well. 现在我们已经找到了解决方案,我们应该与朋友分享,以便他们也可以使用它。 We can do this by forking the repository on Github, and submitting a pull request (the big green button). 我们可以通过在Github上分配存储库并提交拉取请求 (绿色大按钮)来完成此操作。

在此输入图像描述

在此输入图像描述

If you would like to use the fixed code right away without waiting for Winston to merge it, you can type 如果您想立即使用固定代码而无需等待Winston合并它,您可以输入

require(devtools); install_github('robertzk/ggvis')

and the correct version will be installed (but don't do this after this post is a week old, since my fork will probably be out of date). 并且将安装正确的版本(但是这篇文章是一周之后不要这样做,因为我的fork可能已经过时了)。 I have tested it using both ttFunc1 and ttFunc2 and their behavior is identical now. 我已经使用ttFunc1ttFunc2测试了它们,它们的行为现在是相同的。

It is OK to dig into package internals. 可以深入了解包内部。 Never be afraid! 永远不要害怕!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM