我创建了许多输入(参数)的Shiny应用程序.我们的用户希望返回相同的输入值.
我已经检查了这个示例(http://shiny.rstudio.com/articles/client-data.html),它显示通过会话$ clientData $ url_search获取url,但是无法从左侧的sidebarPanel输入生成url.例如:
http://localhost:8100/?obs=10
如何生成一个可以在Shiny中恢复相同值的URL?一个短的应该是最好的,因为有很多输入.
如果我的问题不明确,请告诉我.
谢谢你的任何建议.
Xin*_*Yin 14
为了简单起见,您不必编写任何代码server.R.解析URL查询字符串(例如?obs=10)并设置相应的输入可以通过编写一些javascript代码很好地完成.
下面我将举一个简单的例子,你可以看到如何动态设置Shiny 的任何内置输入控件的值.
shinyUI(
fluidPage(
sidebarLayout(
sidebarPanel(
# wrap input controls into a container so that we can use binding.find()
# function to quickly locate the input controls.
tags$div(id="input_container",
textInput("username", h6("Username:")),
numericInput("age", h6("Age:"),
min=1, max=99, value=20, step=1),
selectInput("sex", h6("Sex:"), choices=c("Male", "Female")),
# load Javascript snippet to parse the query string.
singleton(tags$script(type="text/javascript",
src="js/parse_input.js"))
)
),
mainPanel(
verbatimTextOutput("log")
)
)
)
)
Run Code Online (Sandbox Code Playgroud)
# does nothing but echoes back the user's input values
shinyServer(function(input, output) {
output$log <- renderPrint({
paste("Username: ", input$username, "; Age: ", input$age,
"; Sex: ", input$sex, sep="")
})
})
Run Code Online (Sandbox Code Playgroud)
最后,您需要www/js在Shiny项目目录下创建文件夹,并将此parse_input.js文件放在该js文件夹中.
$(document).ready(function() {
if (window.location.search) {
var input_params = {};
/* process query string, e.g. ?obs=10&foo=bar */
var params = $.map(
window.location.search.match(/[\&\?]\w+=[^\&]+/g),
function(p, i) {
var kv = p.substring(1).split("=");
# NOTE: might have issue to parse some special characters here?
input_params[kv[0]] = decodeURIComponent(kv[1]);
}
);
/* Shiny.inputBindings.getBindings() return the InputBinding instances
for every (native) input type that Shiny supports (selectInput, textInput,
actionButton etc.) */
$.each(Shiny.inputBindings.getBindings(), function(i, b) {
/* find all inputs within a specific input type */
var inputs = b.binding.find('#input_container');
$.each(inputs, function(j, inp) {
/* check if the input's id matches the key specified in the query
string */
var inp_val = input_params[$(inp).attr("id")];
if (inp_val != undefined) {
b.binding.setValue(inp, inp_val);
}
});
});
}
});
Run Code Online (Sandbox Code Playgroud)
然后,您可以使用URL等网站访问该网站http://localhost:7691/?sex=Female&age=44&username=Jane.
您应该在主面板上看到文本变为:
[1] "Username: Jane; Age: 44; Sex: Female"
Run Code Online (Sandbox Code Playgroud)
Bangyou提醒我,我的原始答案(上图)没有解决他的问题.以下是我的第二次回答这个问题的试验.
shinyUI(
fluidPage(
sidebarLayout(
sidebarPanel(
# wrap input controls into a container
tags$div(id="input_container",
textInput("username", h6("Username:")),
numericInput("age", h6("Age:"),
min=1, max=99, value=20, step=1),
selectInput("sex", h6("Sex:"), choices=c("Male", "Female")),
singleton(tags$script(type="text/javascript",
src="js/parse_input.js"))
),
tags$button(type="button", id="save_options",
h6("Save current options")),
tags$input(type="text", style="display:none;", value="{}",
id="inputs_snapshot")
),
mainPanel(
verbatimTextOutput("log"),
verbatimTextOutput("gen_url")
)
)
)
)
Run Code Online (Sandbox Code Playgroud)
# user.saved.snapshots <- list(
# list(sex="Male", age=32, username="Jason"),
# list(sex="Male", age=16, username="Eric"),
# list(sex="Female", age=46, username="Peggy")
# )
#
# save(user.saved.snapshots, file="snapshots.Rdata")
# ^^ Run above code **ONCE** to initiate a dummy data file, storing some possible options.
load("snapshots.Rdata")
renderRestoration <- function(expr, env=parent.frame(), quoted=F) {
func <- exprToFunction(expr)
function() {
func()
# return the selected snapshot to the client side
# Shiny will automatically wrap it into JSOn
}
}
shinyServer(function(input, output, session) {
output$log <- renderPrint({
paste("Username: ", input$username, "; Age: ", input$age,
"; Sex: ", input$sex, "\n\n", "User saved sets: ", str(user.saved.snapshots), sep="")
})
observe({
if (!is.null(input$inputs_snapshot) && length(input$inputs_snapshot) > 0) {
print(input$inputs_snapshot)
user.saved.snapshots[[length(user.saved.snapshots) + 1]] <<- input$inputs_snapshot
save(user.saved.snapshots, file="snapshots.Rdata")
}
})
output$input_container <- renderRestoration({
query <- parseQueryString(session$clientData$url_search)
if (is.null(query$snapshot)) return (list())
sid <- as.numeric(query$snapshot)
if (sid <= length(user.saved.snapshots)) {
user.saved.snapshots[[sid]]
}
})
output$gen_url <- renderPrint({
if (length(input$inputs_snapshot) > 0) {
paste("The current input snapshot is created, and can be restored by visiting: \n",
session$clientData$url_protocol, "://",
session$clientData$url_hostname, ":",
session$clientData$url_port,
session$clientData$url_pathname, "?snapshot=", length(user.saved.snapshots),
sep=""
)
}
})
})
Run Code Online (Sandbox Code Playgroud)
$(document).ready(function() {
if (window.location.search) {
/* METHOD 1: restore from a explicit URL specifying all inputs */
var input_params = {};
/* process query string, e.g. ?obs=10&foo=bar */
var params = $.map(
window.location.search.match(/[\&\?]\w+=[^\&]+/g),
function(p, i) {
var kv = p.substring(1).split("=");
input_params[kv[0]] = decodeURIComponent(kv[1]);
}
);
// you can uncomment this if you want to restore inputs from an
// explicit options specified in the URL in format:
// input_id=value
//restore_snapshot("#input_container", input_params);
}
var restore_snapshot = function(el, input_params) {
/* Shiny.inputBindings.getBindings() return the InputBinding instances
for every (native) input type that Shiny supports (selectInput, textInput,
actionButton etc.) */
$.each(Shiny.inputBindings.getBindings(), function(i, b) {
/* find all inputs within a specific input type */
var inputs = b.binding.find(el);
$.each(inputs, function(j, inp) {
/* check if the input's id matches the key specified in the query
string */
var inp_val = input_params[$(inp).attr("id")];
if (inp_val != undefined) {
b.binding.setValue(inp, inp_val);
}
});
});
}
$("#save_options").on('click', function() {
/* dump all inputs within input container */
var input_params = {}
$.each(Shiny.inputBindings.getBindings(), function(i, b) {
/* find all inputs within a specific input type */
var inputs = b.binding.find('#input_container');
$.each(inputs, function(j, inp) {
/* check if the input's id matches the key specified in the query
string */
var inp_id = $(inp).attr("id");
if (inp_id) {
input_params[inp_id] = b.binding.getValue(inp);
}
});
});
console.log(input_params);
$("#inputs_snapshot").val(JSON.stringify(input_params))
.trigger("change");
});
/* ------------ Shiny Bindings -------------- */
/* First, an input binding monitor change of a hidden input,
* whose value will be changed once the user clicks the
* "save current options" button.
*/
var snapshotBinding = new Shiny.InputBinding();
$.extend(snapshotBinding, {
find: function(scope) {
return $(scope).find("#inputs_snapshot");
},
getValue: function(el) {
return JSON.parse($(el).val());
},
subscribe: function(el, callback) {
$(el).on("change.snapshot", function(e) {
callback();
});
},
unsubscribe: function(el) {
$(el).off(".snapshot");
}
});
Shiny.inputBindings.register(snapshotBinding);
var restoreBinding = new Shiny.OutputBinding();
$.extend(restoreBinding, {
find: function(scope) {
return $(scope).find("#input_container");
},
renderValue: function(el, data) {
// very rudimentary sanity check
if ($.isPlainObject(data) && data.hasOwnProperty('username')) {
restore_snapshot(el, data);
alert("Snapshot restored!");
}
}
});
Shiny.outputBindings.register(restoreBinding, 'inputs.Restore');
});
Run Code Online (Sandbox Code Playgroud)
一个简短的解释:
<input>标记.这允许我们将输入的当前快照发送到服务器.user.saved.snapshots变量,并将其保存到磁盘文件中.?snapshot=[number]可见,服务器将仅向客户端发送有效数据.input$inputs_snapshot列表对象创建显式恢复URL(例如 ?username=Eric&age=44&sex=Male),因为您可以从那里访问所有输入值.我们的javascript也提供了这个功能.有许多细节需要打磨.您可以考虑使用RSQLite包将这些配置文件保存到SQLite数据库.
但上面的演示应该是一个很好的概念证明.