why seq() function in shinyApps does not work? - r

I tried to create a shinyApp with seq() function within the Apps.
header <- dashboardHeader(title = 'Testing' ,titleWidth = 300)
sidebar <- dashboardSidebar(uiOutput("sidebarpanel"), width = 300)
body <- dashboardBody(uiOutput("body"))
uix <- dashboardPage(header, sidebar, body)
serverx <- function(input, output, session){
output$sidebarpanel <- renderUI({
div(
sidebarMenu(id="tabs",
menuItem("Tes 1", tabName = "tes1", icon = icon("dashboard"), selected = TRUE)
)
)
})
output$body <- renderUI({
tabItems(tabItem(tabName = "tes1",
fluidRow(column(2, textInput("s1", "From :", value = 1))
,column(2, textInput("s2", "To", value = 7))
),
textOutput("result")
)
)
})
segment_low <- reactiveValues(ba=NULL)
segment_high <- reactiveValues(ba=NULL)
results <- reactiveValues(ba=NULL)
toListen <- reactive({
list(input$s1, input$s2)
})
observeEvent(toListen(),{
segment_low$ba <- input$s1 %>% as.numeric()
segment_high$ba <- input$s2 %>% as.numeric()
})
observe({
results$ba <- seq(segment_low$ba,segment_high$ba, 1)
})
output$result <- renderText({
results$ba
})
}
shinyApp(uix, serverx)
In that syntax, I would create a variable called results$ba because I want to escalate that value in the next time. But it turns out an error :
Warning: Error in seq.default: 'from' must be of length 1
[No stack trace available]
Could someone help me how to solve this problem? Since this error just happened if I put the reactiveValues to the seq() function, while I input a static input, for example seq(2,5,1) it will not return a error. And I already put the initial value for each input in textInput() function also.
Kindle need your help, developers!
Many Thanks.

The issue is that you're rendering the s1 and s2 inputs server-side. Because of this, the server at the beginning renders them as NULL, and your seq function errors when it gets the NULL value.
The simplest thing to do is to add a req function to prevent your code from evaluating unless it's getting some non-NULL values.
observe({
req(segment_low$ba, segment_high$ba)
results$ba <- seq(segment_low$ba,segment_high$ba, 1)
})
Basically, since you're using observe, which is very eager, you are telling the seq function to evaluate right away. By using the req function, you're stopping the chain of evaluation from happening unless the segment_low$ba and segment_high$ba have non-NULL values.

Related

Saving Dynamic UI to Global R workspace

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.
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) })
}
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.
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 using onStop 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.]

How to correctly use a checkboxInput 'All/None' in a uiOutput context in R Shiny?

Here is the context :
library(shiny)
liste_statut <- c("A","B","C")
ui <- shinyUI(fluidPage(uiOutput("testUI")))
server <- function(input, output, session) {
output$testUI <- renderUI({
navbarPage(
title = "Test",
tabPanel(icon = icon("users"), 'Test',
sidebarPanel(
# Statut
checkboxGroupInput("statut", "Statut", liste_statut, liste_statut),
checkboxInput('selectall_statut', 'Tout / Aucun', T))))
})
# observe({
# updateCheckboxGroupInput(
# session, 'statut', choices = liste_statut,
# selected = if (input$selectall_statut) liste_statut
# )
# })
}
shinyApp(ui = ui, server = server)
I would like to use my checkbox All/None (in comment lines) properly cause in this case i have a "Warning: Error in if: argument is of length zero". Where should i put it or maybe should i redefine properly something in the UI part?
I willingly use the renderUI/uiOutput option (contrary to the "standard mode" ui/server) because in future, i will add an authentification module, so be able to display several "panels" according to user.
Thanks and sorry for my terrible english :).
The following works for me:
library(shiny)
liste_statut <- c("A","B","C")
ui <- shinyUI(fluidPage(uiOutput("testUI")))
server <- function(input, output, session) {
output$testUI <- renderUI({
navbarPage(
title = "Test",
tabPanel(icon = icon("users"), 'Test',
sidebarPanel(
# Statut
checkboxGroupInput("statut", "Statut", liste_statut, liste_statut),
checkboxInput('selectall_statut', 'Tout / Aucun', T))))
})
observeEvent(input$selectall_statut,{
val <- liste_statut
if(!input$selectall_statut)
val <- character(0)
updateCheckboxGroupInput(
session, 'statut',
selected = val
)
})
}
I initially tried selected = ifelse(input$selectall_statut, liste_statut, character(0)) instead of the intermediate variable val. However, ifelse() only returned a single value, not a vector.
If you are going to do this many times over, then I would recommend a custom ifelse function. Perhaps something like the following:
ifelse2 <- function(test, yes, no){
if(test)
return(yes)
return(no)
}

RenderUI with conditional selectInput that dynamically builds more selectInputs in Shiny

I am trying to build a Shiny interface with:
a main selector, which decides:
which submenu (input) to show, which decides:
how many subsequent inputs to show
Here's a minimal reproducible example.
If "First" is chosen from the main selector, then a submenu with two possibilities [1,2] exist. These possibilities result in 1 or 2 subsequent inputs being built. So these possibilities:
If "Second" is chosen from the main selector, then a submenu with two possibilities [3,4] exist. These possibilities result in 3 or 4 subsequent inputs being built.
ui <- fluidPage(
radioButtons(inputId="main_selector",label=h5('Select menu'),
choices = list('First','Second'),selected='First'),
uiOutput("ui_selected")
)
server <- function(input, output, session) {
build_inputs <- function(choices){
output = tagList()
for(i in 1:choices){
output[[i]] = tagList()
output[[i]][[1]] = numericInput(inputId = paste0(i),
label = paste0(i),
value = i)
}
}
# Are these reactive elements necessary? Should they be in the renderUI below?
first_submenu <- reactive({
input$first_submenu
})
second_submenu <- reactive({
input$second_submenu
})
output$ui_selected <- renderUI({
if (input$main_selector == 'First'){
selectInput(inputId = "first_submenu", label="First submenu",
choices=list(1,2))
choices_1 <- first_submenu()
# Build a list of inputs dependent on the choice above
output <- build_inputs(choices_1)
} else if (input$main_selector == 'Second'){
selectInput(inputId = "second_submenu", label="Second submenu",
choices=list(3,4))
choices_2 <- second_submenu()
# Build a list of inputs dependent on the choice above
output <- build_inputs(choices_2)
# Return output as output$ui_selected element
output
})
}
shinyApp(ui, server)
The error I receive is Warning: Error in :: argument of length 0. I believe this is because you can't call the outcome of first_submenu from the renderUI element - but I don't know how to structure my code correctly.
I am not sure whether this is what you are after. The main problem was that your function build_inputs does not return anything. The second problem is that choices from selectInput are not numeric, so you need to convert them beforehand. And one other minor problem, related to the error you mention, is that the elements you want to render exist at the same time, so putting a condition on input$first_submenu will trigger errors (even if it is NULL for a couple of milliseconds), so it's (almost always) good practice to take care of possibly null inputs. The last thing I did was to add another uiOutput for the last layer of dynamic inputs. Hope this helps.
ui <- fluidPage(
radioButtons(inputId="main_selector",label=h5('Select menu'),
choices = list('First','Second'),selected='First'),
uiOutput("ui_selected"),
uiOutput("ui_numeric_inputs")
)
server <- function(input, output, session) {
build_inputs <- function(choices) {
output = tagList()
for(i in 1:choices){
output[[i]] = tagList()
output[[i]][[1]] = numericInput(inputId = paste0(i),
label = paste0(i),
value = i)
}
return(output)
}
output$ui_selected <- renderUI({
if (input$main_selector == 'First'){
selectInput(inputId = "first_submenu", label="First submenu",
choices=c(1,2))
} else if (input$main_selector == 'Second'){
selectInput(inputId = "second_submenu", label="Second submenu",
choices=list(3,4))
}
})
output$ui_numeric_inputs <- renderUI({
if (input$main_selector == 'First' &&
(!is.null(input$first_submenu))) {
build_inputs(as.numeric(input$first_submenu))
} else if (input$main_selector == 'Second' &&
(!is.null(input$second_submenu))){
build_inputs(as.numeric(input$second_submenu))
}
})
}
shinyApp(ui, server)

Error in [: incorrect number of dimensions (while executing Shiny R code)

I am getting errors as "Warning: Error in grepl: invalid 'pattern' argument" and "Error in [: incorrect number of dimensions" (in UI) while executing shiny code. please help. below is the snippet of the code. I am getting error when I am un-commenting last line
library(MASS)
library(shinythemes)
library(shiny)
library(ggplot2)
mass.tmp <- data(package = "MASS")[3]
mass.datasets <- as.vector(mass.tmp$results[,3])
ui <- fluidPage(
theme = shinytheme("superhero"),
titlePanel("Linear Regression Modelling"),
sidebarLayout(
sidebarPanel(
selectInput("dsname", "Dataset:",choices = c(mass.datasets)),
uiOutput("y_axis"),
uiOutput("x_axis")
) ,
mainPanel(
tags$br(),
tags$br(),
"R-squared:",
tags$span(tags$b(textOutput("rsquared")),style="color:blue")
)
)
)
server <- function(input, output) {
output$x_axis <- renderUI({
col_opts <- get(input$dsname)
selectInput("x_axis2", "Independent Variable:", choices = c(names(col_opts)))
})
cols2 <- reactive({
col_opts2 <- get(input$dsname)
#names(col_opts2)[!grepl(input$x_axis2, names(col_opts2))]
})
output$y_axis <- renderUI({
selectInput("y_axis2", "Dependent Variable:", choices = c(names(cols2())))
})
model <- reactive({
#lm(input$dsname[,names(input$dsname) %in% input$y_axis2] ~ input$dsname[,names(input$dsname) %in% input$x_axis2])
#tmp <- paste(input$y_axis2,"~",input$x_axis2,sep = " ")
lm( input$y_axis2 ~ input$x_axis2 , data = input$dsname )
})
model_summary <- reactive({summary(model())})
output$rsquared <- renderText({ model_summary()$r.squared })
}
shinyApp(ui = ui, server = server)
Yes thats better.
There a multiple errors:
We shouldnt debug it all for you, but here are quite some pointers.
That should help you to find them all.
1)
You are using: input$x_axis and input$y_axis but defined it with a "2" at the end. So adapt that.
2)
You should define:
cols2 <- reactive({
col_opts2 <- get(input$dsname)
names(col_opts2)[!grepl(input$x_axis2, names(col_opts2))]
})
outside the renderUI function.
3) Moreover, there seems to be something wrong with this snippet:
names(col_opts2)[!grepl(input$x_axis2, names(col_opts2))]
Finally, I would check if you produce NULLS and prohibit that by !is.null().
Edit: Question update:
You tried to build the lm()formula by strings, which you can test outside of shiny: Will not work.
You should use the formula() function and come up with somethin like:
lm(formula(paste(input$y_axis2, input$x_axis2, sep =" ~ ")), data = get(input$dsname))

Validate() Rshiny

I want to use the validate function in Rshiny.
output$one <- renderTable({
isolate({
Loadprob <- input$prob1
prob <- read.xls(Loadprob$datapath)
validate(need(ncol(prob)==13, "Error"))
But the function validate does not returns the "Error" message and I don't know why.
Thank you!
I created a reproducible example of your code. Please for further questions, try to do it yourself. This makes it a lot easier to find and solve the problem.
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput("prob1", label = "Excel File", multiple = F)
),
mainPanel(
tableOutput("one")
)
)
)
# Server logic
server <- function(input, output) {
output$one <- renderTable({
req(input$prob1)
Loadprob <- input$prob1
prob <- read.csv(Loadprob$datapath, header = T, sep = ";")
## prob <- read.xls(Loadprob$datapath)
validate(need(ncol(prob)==13, "Error"))
prob
})
}
shinyApp(ui, server)
I used read.csv instead of read.xls, as i could not install the xlsx-package, but that shouldnt be the issue.
You also have to include a req() at the beginning of the renderTable function, as it should only be executed, when a file is uploaded.
At the end, you have to tell which variable should be plotted as a table, which in this case is "prob".

Resources