Saving Dynamic UI to Global R workspace
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 textInput
s. The number of textInput
s changes in response to changes in the numericInput
's value. The values of the textInput
s 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 textInput
s are preserved when the UI updates.
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...
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())
...
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:
- 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 usingonStop
to do custom processing as the app terminates.
I'm sure there will be others.
[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.]
Why function `load` not works in `lapply` but works in `for` loops?
load
function is not a good way to assign saved R objects because it loads the object directly in your environment (as you did in your for loop, without assigning a new named object)
saveRDS
and readRDS
would serve you to assign a saved file to a new object in your environment;
val <- c(1:5)
saveRDS(val, file='test1.rds')
val <- c(6:10)
saveRDS(val, file='test2.rds')
files = paste0('test',c(1:2), '.rds')
res <- lapply(files, function(x) readRDS(x))
res
output;
1. 1 2 3 4 5
2. 6 7 8 9 10
How to set environment variables in Jenkins?
This can be done via EnvInject plugin in the following way:
Create an "Execute shell" build step that runs:
echo AOEU=$(echo aoeu) > propsfile
Create an Inject environment variables build step and set "Properties File Path" to
propsfile
.
Note: This plugin is (mostly) not compatible with the Pipeline plugin.
How to configure Shorten command line method for whole project in IntelliJ
You can set up a default way to shorten the command line and use it as a template for further configurations by changing the default JUnit Run/Debug Configuration template. Then all new Run/Debug configuration you create in project will use the same option.
Here is the related blog post about configurable command line shortener option.
Why is message() a better choice than print() in R for writing a package?
TL;DR
You should use cat()
when making the print.*()
functions for S3 objects. For everything else, you should use message()
unless the state of the program is problematic. e.g. bad error that is recoverable gives warning()
vs. show stopping error uses stop()
.
Goal
The objective of this post is to provide feedback on the different output options a package developer has access to and how one should structure output that is potentially on a new object or based upon strings.
R Output Overview
The traditional output functions are:
print()
cat()
message()
warning()
stop()
Now, the first two functions (print()
and cat()
) send their output to stdout
or standard output. The last three functions (message()
, warning()
, and stop()
) send their output to stderr
or the standard error. That is, the result output from a command like lm()
is sent to one file and the error output - if it exists - is sent to a completely separate file. This is particularly important for the user experience as diagnostics then are not cluttering the output of the results in log files and errors are then available to search through quickly.
Designing for Users and External Packages
Now, the above is framed more in a I/O mindset and not necessarily a user-facing frameset. So, let's provide some motivation for it in the context of an everyday R user. In particular, by using 3-5 or the stderr
functions, their output is able to be suppressed without tinkering with the console text via sink()
or capture.output()
. The suppression normally comes in the form of suppressWarnings()
, suppressMessages()
, suppressPackageStartupMessages()
, and so on. Thus, users are only confronted with result facing output. This is particularly important if you plan to allow users the flexibility of turning off text-based output when creating dynamic documents via either knitr, rmarkdown, or Sweave.
In particular, knitr
offers chunk options such as error = F
, message = F
, and warning = F
. This enables the reduction of text accompanying a command in the document. Furthermore, this prevents the need from using the results = "hide"
option that would disable all output.
Specifics of Output
print()
Up first, we have an oldie but a goodie, print()
. This function has some severe limitations. One of them being the lack of embedded concatenation of terms. The second, and probably more severe, is the fact that each output is preceded by [x]
followed by quotations around the actual content. The x
in this case refers to the element number being printed. This is helpful for debugging purposes, but outside of that it doesn't serve any purpose.
e.g.
print("Hello!")
[1] "Hello!"
For concatenation, we rely upon the paste()
function working in sync with print()
:
print(paste("Hello","World!"))
[1] "Hello World!"
Alternatively, one can use the paste0(...)
function in place of paste(...)
to avoid the default use of a space between elements governed by paste()
's sep = " "
parameter. (a.k.a concatenation without spaces)
e.g.
print(paste0("Hello","World!"))
[1] "HelloWorld!"
print(paste("Hello","World!", sep = ""))
[1] "HelloWorld!"
cat()
On the flip side, cat()
addresses all of these critiques. Most notably, the sep=" "
parameter of the paste()
functionality is built in allowing one to skip writing paste()
within cat()
. However, the cat()
function's only downside is you have to force new lines via \n
appended at the end or fill = TRUE
(uses default print width).
e.g.
cat("Hello!\n")
Hello!
cat("Hello","World!\n")
Hello World!
cat("Hello","World!\n", sep = "")
HelloWorld!
It is for this very reason why you should use cat()
when designing a print.*()
S3 method.
message()
The message()
function is one step better than even cat()
! The reason why is the output is distinct from traditional plain text as it is directed to stderr
instead of stdout
. E.g. They changed the color from standard black output to red output to catch the users eye.
Furthermore, you have the built in paste0()
functionality.
message("Hello ","World!") # Note the space after Hello
"Hello World!"
Moreover, message()
provides an error state that can be used with tryCatch()
e.g.
tryCatch(message("hello\n"), message=function(e){cat("goodbye\n")})
goodbye
warning()
The warning()
function is not something to use casually. The warning function is differentiated from the message function primarily by having a line prefixed to it ("Warning message:"
) and its state is consider to be problematic.
Misc: Casual use in a function may inadvertently trigger heartbreak while trying to upload the package to CRAN due to the example checks and warnings normally being treated as "errors".
stop()
Last but not least, we have stop()
. This takes warnings to the next level by completely killing the task at hand and returning control back to the user. Furthermore, it has the most serious prefix with the term "Error:"
being added.
Related Topics
Large Integers in Data.Table. Grouping Results Different in 1.9.2 Compared to 1.8.10
How to Retrieve the Client's Current Time and Time Zone When Using Shiny
Outputting Difftime as Hh:Mm:Ss:Mm in R
How to Load Dependencies in an R Package
Shift a Column of Lists in Data.Table by Group
Calculate Percentages of a Binary Variable by Another Variable in R
Create Several Dummy Variables from One String Variable
Knitr Compile Problems with Rstudio (Windows)
R Data.Table Fread Command:How to Read Large Files with Irregular Separators
How to Make Shiny's Input$Var Consumable for Dplyr::Summarise()
Splitting String Between Capital and Lowercase Character in R
Row Not Consolidating Duplicates in R When Using Multiple Months in Date Filter
How to See All Rows of a Data Frame in a Jupyter Notebook with an R Kernel
How to Apply Dplyr's Select(,Starts_With()) on Rows, Not Columns