将动态 UI 保存到 Global R 工作区 [英] Saving Dynamic UI to Global R workspace

查看:62
本文介绍了将动态 UI 保存到 Global R 工作区的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个可以在 R 工作区中使用的 Shiny 应用程序来为某些代码创建一个用户友好的前端 - 这样某人只需输入并单击一些框,而不是自己创建列表和数据框 - 和然后他们输入的内容将存储在 R 中的工作区中以执行代码.我基本上已经改编了其他人的代码,但无法弄清楚如何保存名为 col 的动态创建的 UI - 它进行文本输入,因此如果人们在其中输入内容,则会保存.

I am trying to create a Shiny App which can be used in the R workspace to create a user friendly front end to some code- so that someone can just type in and click some boxes instead of creating lists and dataframes themselves- and then what they input will be stored in the workspace in R to do the code. I have basically adapted someone else's code but can't work out how I save the dynamically created UI called col - which makes text inputs so if people type something in this is saved.

当我尝试添加某种方式来保存它时,我收到一个错误 Operation not allowed without an active react context.(你试图做一些只能从反应式表达式或观察者内部完成的事情.).代码如下,有没有办法保存文本输入中的信息?

When I try to add some way of saving it I get an error Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.). The code is below, is there a way I can save the information from the text input?

CrossBreakUI <- function(id, number) {

  ns <- NS(id)

  tagList(
    fluidRow(
      column(4,   numericInput(ns("n"), "Number of Groups in Cross-Break", value=5, min=1),     uiOutput(ns("col")))

    )
  )


}


variables <- function(input, output, session, variable.number){
  output$textInput <- renderUI({
    ns <- session$ns
    textInput(ns("textInput"),
              label = "")
  })


  col_names <- reactive(paste0("col", seq_len(input$n)))
  output$col <- renderUI({
    ns <- session$ns
    map(col_names(), ~ textInput(ns(.x), NULL))
  })


  reactive({



    # Create Pair: variable and its value
    df <- data.frame(
      "variable.number" = variable.number,

      stringsAsFactors = FALSE
    )



  })



}

ui <- fixedPage(

  div(
    CrossBreakUI("var1", 1)



  ))


server <- function(input, output) {




  add.variable <- reactiveValues()
  add.variable$df <- data.frame(
    "n" = numeric(0),
    "col" = character(0),
    stringsAsFactors = FALSE

  )


   var1 <- callModule(variables, paste0("var", 1), 1)



  observeEvent(input[[NS(paste0("var", 1), "n")]], {
    add.variable$df[1,1] <- input[[NS(paste0("var", 1), "n")]]
  })
**#THIS IS THE ERROR- IT DOES NOT SAVE THE TEXT INPUT FROM THIS VARIABLE**  
  observeEvent(input[[NS(paste0("var", 1), "col")]], {
    add.variable$df[1,2] <- input[[NS(paste0("var", 1), "col")]]
  })


observe({
    assign(x ="CrossBreak", value=add.variable$df, envir= .GlobalEnv) })

}

推荐答案

第二次修订

如果我的理解是正确的,我认为这与您想要的很接近.你有一个 numericInput.UI 呈现一系列 textInput.textInput 的数量随着 numericInput 值的变化而变化.textInput 的值被保存到全局环境中的一个变量中(并且这个全局变量的值在应用程序终止时打印到控制台).已在 textInput 中输入的值会在 UI 更新时保留.

Second revision

If my understanding is correct, I think this gets close to what you want. You have a numericInput. The UI presents a series of textInputs. The number of textInputs changes in response to changes in the numericInput's value. The values of the textInputs are saved to a variable in the global environment (and the value of this global variable is printed to the console as the app terminates). Values already entered in the textInputs are preserved when the UI updates.

我的解决方案与您的不同,因为您有一个模块试图控制每个 textInput,还有一个主服务器尝试询问每个 textInput 以获取其值.我使用单个模块的多个实例,每个 textInput 一个.每个模块实例独立于所有其他实例管理其 textInput 值的持久性.

My solution differs from yours in that you have one module attempting to control every textInput and a main server that attempts to interrogate each textInput to obtain its value. I use multiple instances of a single module, one for each textInput. Each module instance manages the persistence of its textInput's value independently of all the other instances.

library(shiny)

groupList <- list()

# Module to define a self-contained "write-my-value" textInput 
writeMyValueTextInputUI <- function(id, idx) {
  ns <- NS(id)

  textInput(ns("groupName"), paste0("Group ", idx))
}

writeMyValueTextInput <- function(input, output, session, id) {
  ns <- session$ns

  # Initialise
  observe ({
    id <- as.numeric(id)
    if (id <= length(groupList)) updateTextInput(session, "groupName", value=groupList[[id]])
  })

  observeEvent(input$groupName, {
    req(input$groupName)
    # Note global assignment
    groupList[[id]] <<- input$groupName
  })

  rv <- reactive ({
    input$groupName
  })

  return(rv)
}

ui <- fluidPage(
   titlePanel("Crossbreak demo"),
   sidebarLayout(
      sidebarPanel(
        numericInput("groupCount", "Number of groups in cross-break:", min=1, value=5),
      ),
      mainPanel(
        textOutput("groupCount"),
        uiOutput("groupList")
      )
   )
)

server <- function(input, output, session) {
  onStop(function() cat(paste0(groupList, collapse=", ")))
  ns <- session$ns

  controllers <- list()

  output$groupList <- renderUI({
    req(input$groupCount)
    textInputs <- lapply(
                    1:input$groupCount,
                    function(x) {
                      id <- ns(paste0("text", x))
                      controllers[[x]] <- callModule(writeMyValueTextInput, id, x)
                      return(writeMyValueTextInputUI(id, x))
                     }
                  )
    do.call(tagList, textInputs)
  })
}

shinyApp(ui = ui, server = server)

==========================
我没有试过运行你的代码(这不是一个简单的自包含示例),但以下只是从控制台运行应用程序的一种方式.(这就是你所说的来自全球环境?)...

=========================
I haven't tried running your code (it's not really a simple self-contained example), but the following is just one way of running an app from the console. (is that what you mean when you say "from the global environment?)...

myList <- list(
  ui = bootstrapPage(
    numericInput('n', 'Number of obs', 100),
    plotOutput('plot')
  ),
  server = function(input, output) {
    output$plot <- renderPlot({ hist(runif(input$n)) })
  }
)

if (interactive()) runApp(myList)

我的代码基于 此页面还有其他例子...

I based my code on this page which also has other examples...

请注意,如果您在批处理作业中运行 R 脚本,则无法执行此操作,因为批处理作业没有显示应用程序的上下文.因此 if (interactive())...

Note that you can't do this if you're running an R script in a batch job, as the batch job has no context in which to display the app. Hence if (interactive())...

好的.响应 OP 的澄清,这里有一个非常粗略的演示,展示了一种做她想做的事情的方式.请注意在 observeEvent 中使用了全局赋值运算符 (<<-).

OK. Responding to OP's clarification, here's a really crude demonstraion of one way of doing what she wants. Note the use of the global assignment operator (<<-) in the observeEvent.

x <- NA
print(paste0("globalValue is currently: ", x))

myList <- list(
  ui = bootstrapPage(
    numericInput('n', 'Please give me a number', 100)
  ),
  server = function(input, output) {
    observeEvent(input$n, {x <<- input$n})
  }
)

if (interactive()) runApp(myList)
print(paste0("globalValue is now: ", x))

在我的系统上,在控制台中逐步执行这些语句会给出:

On my system, stepping through these statements in the console gives:

> x <- NA
> print(paste0("globalValue is currently: ", x))
[1] "globalValue is currently: NA"
> myList <- list(
+   ui = bootstrapPage(
+     numericInput('n', 'Please give me a number', 100)
+   ),
+   server = function(input, output) {
+     observeEvent(input$n, {x <<- input$n})
+   }
+ )
> if (interactive()) runApp(myList)

Listening on http://127.0.0.1:4429

> print(paste0("globalValue is now: ", x))
[1] "globalValue is now: 104"
> 

显然,这不是一个现实的生产解决方案.可能的解决方案可能包括:

Obviously, this isn't a realistic production solution. Possible solutions might include:

  • 写入应用中的临时 Rds 文件,然后在应用终止后读取该文件.
  • 使用 session$userData 在应用运行时存储所需信息,然后使用 onStop 在应用终止时进行自定义处理.
  • Writing to a temporary Rds file in the app and then reading it in once the app terminates.
  • Using session$userData to store the required information whilst the app is running and then using onStop to do custom processing as the app terminates.

我相信还会有其他人.

[OP:顺便说一句,看看我的代码与你的相比的长度.让自己站在愿意提供解决方案的人的立场上.他们最有可能回答谁的问题:你的还是我的?提供简洁、相关的代码使您更有可能得到有用的答复.]

[OP: As an aside, look at the length of my code compared to yours. Put yourself in the shoes of someone who's willing to provide solutions. Whose question are they most likely to answer: yours or mine? Providing compact, relevant code makes it far more likely you'll get a useful reply.]

这篇关于将动态 UI 保存到 Global R 工作区的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆