Может ли приложение Shiny отвечать на элемент управления, не обращаясь к серверу?

Я пишу виджет rgl в рамках htmlwidgets, чтобы rgl сцены можно было использовать для вывода в приложениях Shiny. В основном все работает (хотя все еще грубо; см. Пакет rglwidget на http://R-forge.r-project.org), но он не так отзывчив, как встроенные элементы управления Javascript, которые уже есть в rgl. Я подозреваю, что проблема в обратном пути к серверу.

Иногда это будет неизбежно: если вы хотите внести большие изменения в сцену, вы можете выполнить много вычислений в R. Но в других случаях (тех, которые охватываются встроенными элементами управления), R не нужно вовлечены, все можно сделать на Javascript.

Я не хочу дублировать всю работу, которая ушла на написание элементов управления вводом Shiny, но я бы хотел их использовать. Итак, мой вопрос:

Есть ли способ указать входу Shiny вызывать функцию Javascript при его изменении, а не отправлять его значение на сервер для использования в выходных данных Shiny?


person user2554330    schedule 04.09.2015    source источник


Ответы (1)


Ответ на мой вопрос - «Да»! На самом деле это довольно просто, по крайней мере, если элемент управления использует структуру htmlwidgets.

Предупреждение: я не очень разбираюсь в Javascript, поэтому это может быть не очень хороший стиль Javascript. Пожалуйста, дайте мне знать, если да, и я исправлю это.

Вот идея: если у меня есть элемент управления Shiny sliderInput() с inputId = "foo", то мой собственный код Javascript может получить его с помощью window["foo"] и установить для него обработчик событий onchange. Когда этот обработчик событий запускается, я могу прочитать свойство «value» и отправить его своим элементам управления.

Если я не использую реактивные входы от ползунка, я не получаю задержки от перехода на сервер.

Вот моя текущая функция renderValue для виджета:

renderValue: function(el, x, instance) {
  var applyVals = function() {

    /* We might be running before the scene exists.  If so, it
       will have to apply our initial value. */

      var scene = window[x.sceneId].rglinstance;
      if (typeof scene !== "undefined") {
        scene.applyControls(x.controls);
        instance.initialized = true;
      } else {
        instance.controls = x.controls;
        instance.initialized = false;
      }
    };

  el.rglcontroller = instance;

  if (x.respondTo !== null) {
    var control = window[x.respondTo];
    if (typeof control !== "undefined") {
      var self = this, i, oldhandler = control.onchange;
      control.onchange = function() {
        for (i=0; i<x.controls.length; i++) {
          x.controls[i].value = control.value;
        }
        if (oldhandler !== null)
          oldhandler.call(this);
        applyVals();
      };
      control.onchange();
    }
  }
  applyVals();
},
person user2554330    schedule 05.09.2015