Shiny 应用程序可以在不访问服务器的情况下响应控件吗?
Can a Shiny app respond to a control without going to the server?
我正在 htmlwidgets 框架内编写一个 rgl
小部件,以便 rgl
场景可用于 Shiny 应用程序中的输出。事情基本上可以正常工作(尽管它的边缘仍然很粗糙;请参阅 http://R-forge.r-project.org 上的 rglwidget
包),但它的响应速度不如 [=] 中已有的本机 Javascript 控件10=]。
我怀疑问题是到服务器的往返。
有时这将是不可避免的:如果您想对场景进行大的更改,您可能需要在 R 中进行大量计算。但在其他情况下(本机控件涵盖的那些),没有需要R参与,一切都可以在Javascript.
中完成
我不想重复编写 Shiny 输入控件的所有工作,但我想使用它们。所以我的问题是:
有没有办法告诉 Shiny 输入在更改时调用 Javascript 函数,而不是将其值发送到服务器以在 Shiny 输出中使用?
我的问题的答案是 "Yes"!它实际上相当简单,至少如果控件使用 htmlwidgets
框架。
警告:我对 Javascript 不是很有经验,所以这可能不是很好的 Javascript 风格。如果有请告诉我,我会改进的。
思路是这样的:如果我有一个带 inputId = "foo"
的 Shiny sliderInput()
控件,那么我自己的 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();
},
我正在 htmlwidgets 框架内编写一个 rgl
小部件,以便 rgl
场景可用于 Shiny 应用程序中的输出。事情基本上可以正常工作(尽管它的边缘仍然很粗糙;请参阅 http://R-forge.r-project.org 上的 rglwidget
包),但它的响应速度不如 [=] 中已有的本机 Javascript 控件10=]。
我怀疑问题是到服务器的往返。
有时这将是不可避免的:如果您想对场景进行大的更改,您可能需要在 R 中进行大量计算。但在其他情况下(本机控件涵盖的那些),没有需要R参与,一切都可以在Javascript.
中完成我不想重复编写 Shiny 输入控件的所有工作,但我想使用它们。所以我的问题是:
有没有办法告诉 Shiny 输入在更改时调用 Javascript 函数,而不是将其值发送到服务器以在 Shiny 输出中使用?
我的问题的答案是 "Yes"!它实际上相当简单,至少如果控件使用 htmlwidgets
框架。
警告:我对 Javascript 不是很有经验,所以这可能不是很好的 Javascript 风格。如果有请告诉我,我会改进的。
思路是这样的:如果我有一个带 inputId = "foo"
的 Shiny sliderInput()
控件,那么我自己的 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();
},