如何在R Shiny中从变量存储的表达式创建观察者?
问题:Shiny中如何从字符串生成观察者并正确传递表达式
先看背景:我们有一个基础Shiny应用,输入姓名时控制台会实时输出问候内容:
library(shiny) ui <- fluidPage( textInput("my_name", "Enter your name") ) server <- function(input, output) { observe(print(paste('Hello', input$my_name))) } shinyApp(ui = ui, server = server)
当输入"Batman"时,控制台会输出:
Listening on http://127.0.0.1:5143 [1] "Hello " [1] "Hello Ba" [1] "Hello Batm" [1] "Hello Batman"
现在需要写一个函数,从字符串变量生成对应的观察者。最初尝试的写法无法生效:
observe_from_string <- function(x) { x_as_expr <- str2lang(x) o <- observe(x_as_expr, env = parent.frame(2)) invisible(o) } # 在server中调用 server <- function(input, output) { x <- "print(paste('Hello', input$my_name))" observe_from_string(x) }
原因是observe会把x_as_expr当作字面表达式执行,而不是解析它内部存储的调用逻辑,相当于直接写observe(x_as_expr),自然无法获取input$my_name的值。
虽然用eval(parse())能实现功能,但属于hack写法:
observe_from_string_hack <- function(x) { o <- eval(parse(text = paste("observe(", x, ")")), envir = parent.frame()) invisible(o) }
正确实现方法
方法1:构造调用对象并执行
通过call()构造observe()的调用结构,再在父环境中执行,就能让observe拿到正确的表达式:
library(shiny) observe_from_string <- function(x) { # 将字符串转为表达式 target_expr <- str2lang(x) # 构造observe的调用:observe(target_expr) observe_call <- call("observe", target_expr) # 在父环境(server函数的环境)中执行这个调用 observer <- eval(observe_call, envir = parent.frame()) invisible(observer) }
方法2:用rlang的非标准求值(推荐)
借助rlang包的inject()和!!操作符,可以更简洁地实现表达式注入:
library(shiny) library(rlang) observe_from_string <- function(x) { target_expr <- str2lang(x) # 把target_expr的内容注入到observe的参数中 observer <- inject(observe(!!target_expr)) invisible(observer) }
验证示例
完整应用代码:
library(shiny) library(rlang) # 方法2需要,方法1可移除 observe_from_string <- function(x) { target_expr <- str2lang(x) observer <- inject(observe(!!target_expr)) invisible(observer) } ui <- fluidPage( textInput("my_name", "Enter your name") ) server <- function(input, output) { x <- "print(paste('Hello', input$my_name))" observe_from_string(x) } shinyApp(ui = ui, server = server)
运行后输入姓名,控制台会和基础应用一样输出实时问候,且写法更规范,避免了字符串解析的潜在风险。
内容的提问来源于stack exchange,提问作者Geoffrey Poole
相关产品推荐
相关产品推荐

