如何从 timevis 中的可拖动垂直条中获取日期?

How can I get the date from the draggable vertical bar in timevis?

我发现 timevis 包对于显示时间段非常有用,它可以按预期显示我的数据。

我已经使用 addCustomTime() 函数添加了一条可拖动的垂直线,这也很好用,但是,我发现我无法获取该线的当前值。

底层的js小部件公开了与元素交互的函数,但我一直无法成功访问它们。我曾尝试使用 shinyjs 来捕捉时间线发出的事件,但我也无法让它发挥作用。

我对 R 和 shiny 相当满意,但在 js 方面是一个新手,所以我怀疑我只是错过了一个技巧。

在我的 ui.R 中,我创建了一个 timevisOutput 对象:

fluidPage(
  timevisOutput("timeline")
)

我正在尝试收听 "timechanged" 事件:http://visjs.org/docs/timeline/#Events

当我在 server.R 中使用 shinyjs 添加侦听器时,我能够看到 "mouseenter" 事件,但 "timechanged" 处理程序似乎没有触发:

onevent("mouseenter", "timeline", print("timeline: mouseenter"))
onevent("timechanged", "timeline", print("Saw timechanged!"))

visjs 文档在 "how to listen for a select event":

上有这个片段
timeline.on('select', function (properties) {
  alert('selected items: ' + properties.items);
});

我尝试在 shinyjs::extendShinyjs() 调用中添加它,但这也不起作用。我添加了一个示例来监听有效的按键事件:

$(document).keypress(function(e) { alert('Key pressed: ' + e.which); });

这让我觉得我没有正确引用时间轴对象。那里的 $(document) 让我觉得我不知道如何正确获取时间轴元素。

因为我可以看到时间轴的 mouseenter 事件,但看不到它的时间更改事件,我想我需要 .on() 调用,但是,我认为我没有正确引用时间轴元素/.

在@timelyportfolio 的第一个回复之后:

写得很棒,谢谢!我能够确认事件的外观;我已经能够收听 _selected 事件,但很高兴看到。

我正在尝试获取一个自定义时间值,它在图表中使用了一条可拖动的线,它是这样添加的:

addCustomTime("mytimevis", Sys.Date(), "CustomTimeId")

底层 javascript 小部件 (http://visjs.org/docs/timeline) 的文档显示了一个事件 ("timechanged") 和一个方法 ("getCustomTime()") 来访问它值,但我没有在跟踪中看到事件,也无法弄清楚如何使方法调用起作用。

"timechanged" 事件没有出现在跟踪中,这让我觉得我需要从小部件的文档中调用 .on() 以启用该事件:

timeline.on('select', function (properties) {
  alert('selected items: ' + properties.items);
});

我一直在努力让该方法调用起作用,因为理想情况下我想捕获它的变化,而且如果我能让它起作用,我应该能够让 getCustomTime() 方法作为好吧!

假设

我会假设您想要获取 R 中的数据来回答。如果我假设不正确,请告诉我,我会调整。

与 shiny.trace

的消息

如果您不知道,我经常使用的一个技巧是 options(shiny.trace=TRUE),它会在您的屏幕上显示通过 websocket 发送的所有消息。这是我 运行 以下代码时看到的内容。

library(timevis)

data <- data.frame(
  id      = 1:4,
  content = c("Item one"  , "Item two"  ,"Ranged item", "Item four"),
  start   = c("2016-01-10", "2016-01-11", "2016-01-20", "2016-02-14 15:00:00"),
  end     = c(NA          ,           NA, "2016-02-04", NA)
)

tv <- timevis(data)

# now let's see what messages we get in Shiny
library(shiny)
options(shiny.trace=TRUE)

ui <- timevisOutput("mytimevis")
server <- function(input,output,session) {
  output$mytimevis <- renderTimevis({tv})
}

shinyApp(ui,server)

我突出显示了与 select 有关的消息。这告诉我们可以观察或对 mytimevis_selected.

做出反应

观察select事件

现在,让我们关闭 options(shiny.trace=FALSE) 并在收到 selected 消息时在 R 控制台中打印。

options(shiny.trace=FALSE)

ui <- timevisOutput("mytimevis")
server <- function(input,output,session) {
  output$mytimevis <- renderTimevis({tv})

  observeEvent(
    input$mytimevis_selected,
    {
      print(input$mytimevis_selected)
    }
  )
}

shinyApp(ui,server)

添加时间更改处理程序

根据评论和重新阅读您的问题,我现在了解到您想为 timechanged 添加一个事件处理程序。我认为这段代码将帮助您渡过难关。

library(timevis)
library(shiny)
library(htmltools)
library(htmlwidgets)
library(magrittr)

tv <- timevis() %>%
  addCustomTime(Sys.Date() - 1, "yesterday") %>%
  # add an event handler since this is not one
  #   timevis provides
  htmlwidgets::onRender(
"
function(el,x) {
  // this will be our instance
  var tv = this.timeline;

  tv.on('timechanged', function(id, time) {
    //uncomment debugger if you want to stop here
    //debugger;
    Shiny.onInputChange(el.id + '_timechanged', {id:id, time:time})
  });
}
"
  )


shinyApp(
  ui = fluidPage(
    timevisOutput("timeline"),
    actionButton("btn", "Add time bar 24 hours ago")
  ),
  server = function(input, output) {
    output$timeline <- renderTimevis(
      tv
    )
    observeEvent(input$timeline_timechanged, {
      str(input$timeline_timechanged)
    })
  }
)

跟进

如果我走错了路或者 none 这是否有意义,请告诉我。我感受到你的痛苦,对这种混合技术进行故障排除可能非常棘手。

(免责声明:我写了 timevis 包)

首先:Kenton 的回答非常好,OP 的尝试也很棒。

这更像是一个信息 post。

为什么您尝试的方法不起作用

onevent("timechanged", ...) 不起作用的原因是因为 onevent() 函数适用于任何标准 javascript 事件,但它不适用于不同插件提供的特定事件。

要绑定诸如此类的自定义事件,您需要编写一些简短的自定义 javascript 代码。因此,查看 visjs 文档并找到 timeline.on('select', ...) 代码是正确的路径,但是(正如您所怀疑的那样)您没有正确引用时间轴对象。

出于这个原因,我确保导出实际的底层对象,以便像您这样的人可以以原始格式操作它。要从 timevis 访问时间线对象,请使用 $(id)[0].widget.timeline。因为你的时间线的 id 是 timeline 并且你想调用 on() 函数,那么你可以做类似 $("#timeline")[0].widget.timeline.on('select', ...).

的事情

为什么这个功能不存在

当我查看 visjs 的文档时,我决定有 4 个事件,只要它们发生变化,小部件就会自动导出到 shiny:数据、所选项目、所有项目的 ID 和当前可见时间 window。我决定不使用 on('timechanged') 事件,因为它的行为与其他事件不同:它一次只会 return 一项。

这是什么意思?比如input$timeline_data总是returns整个数据在这一刻; input$timeline_select 总是告诉你此时所有选中的项目是什么; input$timeline_timechanged 每次拖动不同的垂直条时都会被触发,因此只会包含最后拖动的条,而不是所有条,并且它不会让您在一个特定的条中询问时间.这有意义吗?

当然可以编写一些代码来跟踪所有柱状图,但这很快就会变得非常混乱(例如,必须确保在柱状图 added/removed).所以我选择不去那个兔子洞。