Unexpected values from text input in Rshiny - r

I am making an app that takes input from a slider to create the matching number of input text boxes. However, when I print the values from the input boxes it does not always update.
Example:
Pick 3 on slider input. Put 1,2,3 into the 3 text boxes respectively.Hit submit. Prints number = 1 number = 2 number = 3. When I move the slider to 2 and hit enter, I get number = 1 number = 2 despite no values being in the text input anymore. If I move the slider to 4, I will than get the output number = NA number = NA number = 3 number = NA.
Clearly it is remembering previous input values, but I cannot understand why or how to fix it.
ui.R
shinyUI(fluidPage(
fluidRow(
column(4, wellPanel(
sliderInput("numObs", "Number of observations", 1, 30, 3),
uiOutput("buttons"),
submitButton(text = "Apply Changes", icon = NULL)
)),
column(8,
textOutput("a")
)
)
))
server.R
shinyServer(function(input, output) {
output$buttons <- renderUI({
obs <- input$"numObs";
objs <-list();
for (i in 1:obs ){
objs <- list(objs, numericInput(inputId = paste0("t", i), "Day:", NA),br());
}
objs <- fluidRow(objs);
})
t<- function(){
for(i in 1:input$"numObs"){
if(i ==1){
t <- c(as.numeric(input[[paste0("t",i)]])[1]);
}
else{
t <- c(t,as.numeric(input[[paste0("t",i)]])[1]);
}
}
return(t);
}
output$a<- renderText({
paste("number = ", t());
})
})

I made some changes and introduced a few things to your code. Its better to use actionButton than the submitButton as it is more flexible. If you dont like the style of the actionButton, look into Shiny Themes
rm(list = ls())
library(shiny)
ui =(fluidPage(
fluidRow(
column(4, wellPanel(
sliderInput("numObs", "Number of observations", 1, 30, 3),
uiOutput("buttons"),
actionButton("goButton", "Apply Changes")
)),
column(8,textOutput("a"))
) ))
server = (function(input, output) {
output$buttons <- renderUI({
obs <- input$"numObs";
objs <-list();
for (i in 1:obs ){
objs <- list(objs, numericInput(inputId = paste0("t", i), "Day:", NA),br());
}
objs <- fluidRow(objs);
})
# keep track if the Number of obseervations change
previous <- eventReactive(input$goButton, {
input$numObs
})
t <- eventReactive(input$goButton, {
for(i in 1:input$"numObs"){
if(i ==1){
t <- c(as.numeric(input[[paste0("t",i)]])[1]);
}
else{
t <- c(t,as.numeric(input[[paste0("t",i)]])[1]);
}
}
return(t)
})
output$a<- renderText({
if(previous() != input$numObs){
return()
}
paste("number = ", t());
})
})
runApp(list(ui = ui, server = server))

Related

How to add a spinner before a selectizeInput has loaded all the choices? [Shiny]

I want to make an app with 2 actionButtons: 1) to submit the changes before loading a selectizeInput and 2) to draw the plot.
I know how to add a spinner after clicking a actionButton but the majority of the cases is added when you want to show the plot.
However, is it possible to add a spinner without showing any plot?
In this particular case, I want to show a spinner after clicking "Submit" until the selectizeInput from the 'Selection tab' is loaded. As you can see the example that I attach, it takes a bit to load all the choices (since the file has 25000 rows).
I already have one spinner after clicking the second actionButton (Show the plot) but I need one more.
I have created an example, but for some reason the plot is not shown in the shiny app and it appears in the window from R (I don't know why but I added the plot just to show you how I put the second spinner. I want a similar one but with the first actionButton.).
library(shiny)
library(shinycssloaders)
ui <- fluidPage(
titlePanel("My app"),
sidebarLayout(
sidebarPanel(
tabsetPanel(
tabPanel("Submit",
checkboxInput("log2", "Log2 transformation", value = FALSE),
actionButton("submit", "Submit")
),
tabPanel("Selection",
br(),
selectizeInput(inputId = "numbers", label = "Choose one number:", choices=character(0)),
actionButton("show_plot", "Show the plot")
))
),
mainPanel(
conditionalPanel(
condition = "input.show_plot > 0",
style = "display: none;",
withSpinner( plotOutput("hist"),
type = 5, color = "#0dc5c1", size = 1))
)
)
)
server <- function(input, output, session) {
data <- reactive({
data = read.csv("https://people.sc.fsu.edu/~jburkardt/data/csv/hw_25000.csv")
data[,1] <- as.character(data[,1])
if(input$log2 == TRUE){
cols <- sapply(data, is.numeric)
data[cols] <- lapply(data[cols], function(x) log2(x+1))
}
return(data)
})
mylist <- reactive({
req(data())
data <- data()
data <- data[,1]
return(data)
})
# This is to generate the choices (gene list) depending on the user's input.
observeEvent(input$submit, {
updateSelectizeInput(
session = session,
inputId = "numbers",
choices = mylist(), options=list(maxOptions = length(mylist()))
)
})
v <- reactiveValues()
observeEvent(input$show_plot, {
data <- data()
v$plot <- plot(x=data[,1], y=data[,2])
})
# If the user didn't choose to see the plot, it won't appear.
output$hist <- renderPlot({
req(data())
if (is.null(v$plot)) return()
if(input$show_plot > 0){
v$plot
}
})
}
Does anyone know how to help me, please?
Thanks very much
It's a little tricky.
First of all I'd update the selectizeInput on the server side as the warning suggests:
Warning: The select input "numbers" contains a large number of
options; consider using server-side selectize for massively improved
performance. See the Details section of the ?selectizeInput help
topic.
Furthermore I switched to ggplot2 regarding the plotOutput - Please see this related post.
To show the spinner while the selectizeInput is updating choices we'll need to know how long the update takes. This information can be gathered via shiny's JS events - please also see this article.
Finally, we can show the spinner for a non-existent output, so we are able to control for how long the spinner is shown (see uiOutput("dummyid")):
library(shiny)
library(shinycssloaders)
library(ggplot2)
ui <- fluidPage(
titlePanel("My app"),
tags$script(HTML(
"
$(document).on('shiny:inputchanged', function(event) {
if (event.target.id === 'numbers') {
Shiny.setInputValue('selectizeupdate', true, {priority: 'event'});
}
});
$(document).on('shiny:updateinput', function(event) {
if (event.target.id === 'numbers') {
Shiny.setInputValue('selectizeupdate', false, {priority: 'event'});
}
});
"
)),
sidebarLayout(
sidebarPanel(
tabsetPanel(
tabPanel("Submit",
checkboxInput("log2", "Log2 transformation", value = FALSE),
actionButton("submit", "Submit")
),
tabPanel("Selection",
br(),
selectizeInput(inputId = "numbers", label = "Choose one number:", choices=NULL),
actionButton("show_plot", "Show the plot")
))
),
mainPanel(
uiOutput("plotProxy")
)
)
)
server <- function(input, output, session) {
previousEvent <- reactiveVal(FALSE)
choicesReady <- reactiveVal(FALSE)
submittingData <- reactiveVal(FALSE)
observeEvent(input$selectizeupdate, {
if(previousEvent() && input$selectizeupdate){
choicesReady(TRUE)
submittingData(FALSE)
} else {
choicesReady(FALSE)
}
previousEvent(input$selectizeupdate)
})
data <- reactive({
data = read.csv("https://people.sc.fsu.edu/~jburkardt/data/csv/hw_25000.csv")
if(input$log2 == TRUE){
cols <- sapply(data, is.numeric)
data[cols] <- lapply(data[cols], function(x) log2(x+1))
}
return(data)
})
mylist <- reactive({
req(data()[,1])
})
observeEvent(input$submit, {
submittingData(TRUE)
reactivePlotObject(NULL) # reset
updateSelectizeInput(
session = session,
inputId = "numbers",
choices = mylist(), options=list(maxOptions = length(mylist())),
server = TRUE
)
})
reactivePlotObject <- reactiveVal(NULL)
observeEvent(input$show_plot, {
reactivePlotObject(ggplot(data(), aes_string(x = names(data())[1], y = names(data())[2])) + geom_point())
})
output$hist <- renderPlot({
reactivePlotObject()
})
output$plotProxy <- renderUI({
if(submittingData() && !choicesReady()){
withSpinner(uiOutput("dummyid"), type = 5, color = "#0dc5c1", size = 1)
} else {
conditionalPanel(condition = "input.show_plot > 0", withSpinner(plotOutput("hist"), type = 5, color = "#0dc5c1", size = 1), style = "display: none;")
}
})
}
shinyApp(ui, server)
First 100 rows of your example data (dput(head(data, 100)) - your link might be offline some day):
structure(list(Index = 1:100, Height.Inches. = c(65.78331, 71.51521,
69.39874, 68.2166, 67.78781, 68.69784, 69.80204, 70.01472, 67.90265,
66.78236, 66.48769, 67.62333, 68.30248, 67.11656, 68.27967, 71.0916,
66.461, 68.64927, 71.23033, 67.13118, 67.83379, 68.87881, 63.48115,
68.42187, 67.62804, 67.20864, 70.84235, 67.49434, 66.53401, 65.44098,
69.5233, 65.8132, 67.8163, 70.59505, 71.80484, 69.20613, 66.80368,
67.65893, 67.80701, 64.04535, 68.57463, 65.18357, 69.65814, 67.96731,
65.98088, 68.67249, 66.88088, 67.69868, 69.82117, 69.08817, 69.91479,
67.33182, 70.26939, 69.10344, 65.38356, 70.18447, 70.40617, 66.54376,
66.36418, 67.537, 66.50418, 68.99958, 68.30355, 67.01255, 70.80592,
68.21951, 69.05914, 67.73103, 67.21568, 67.36763, 65.27033, 70.84278,
69.92442, 64.28508, 68.2452, 66.35708, 68.36275, 65.4769, 69.71947,
67.72554, 68.63941, 66.78405, 70.05147, 66.27848, 69.20198, 69.13481,
67.36436, 70.09297, 70.1766, 68.22556, 68.12932, 70.24256, 71.48752,
69.20477, 70.06306, 70.55703, 66.28644, 63.42577, 66.76711, 68.88741
), Weight.Pounds. = c(112.9925, 136.4873, 153.0269, 142.3354,
144.2971, 123.3024, 141.4947, 136.4623, 112.3723, 120.6672, 127.4516,
114.143, 125.6107, 122.4618, 116.0866, 139.9975, 129.5023, 142.9733,
137.9025, 124.0449, 141.2807, 143.5392, 97.90191, 129.5027, 141.8501,
129.7244, 142.4235, 131.5502, 108.3324, 113.8922, 103.3016, 120.7536,
125.7886, 136.2225, 140.1015, 128.7487, 141.7994, 121.2319, 131.3478,
106.7115, 124.3598, 124.8591, 139.6711, 137.3696, 106.4499, 128.7639,
145.6837, 116.819, 143.6215, 134.9325, 147.0219, 126.3285, 125.4839,
115.7084, 123.4892, 147.8926, 155.8987, 128.0742, 119.3701, 133.8148,
128.7325, 137.5453, 129.7604, 128.824, 135.3165, 109.6113, 142.4684,
132.749, 103.5275, 124.7299, 129.3137, 134.0175, 140.3969, 102.8351,
128.5214, 120.2991, 138.6036, 132.9574, 115.6233, 122.524, 134.6254,
121.8986, 155.3767, 128.9418, 129.1013, 139.4733, 140.8901, 131.5916,
121.1232, 131.5127, 136.5479, 141.4896, 140.6104, 112.1413, 133.457,
131.8001, 120.0285, 123.0972, 128.1432, 115.4759)), row.names = c(NA,
100L), class = "data.frame")

R Shiny Application Conditional calculations and panel with condition on the output

I am new to Shiny. What I want to do in my application is, running & displaying some part of the code only when a condition on another calculation is met.
The conditionalPanel works fine with the conditions on input values but I could not figure out how to do this with the 'output' values, i.e., conditionally on the output values of the functions. Below is my example code:
library(shiny)
msLocation <- "msLoc"
searchMWText <- "searchMW"
bid <- "2333333"
fulltext <- "fullDisplay"
ui <- fluidPage(
titlePanel("Run server codes conditionally"),
sidebarLayout(
sidebarPanel(
helpText("Evaluate input and run different parts of the code depending on the output functions"),
br(),
sliderInput("rand", "select seed", min = 1, max = 50, step = 1, value = 1)
),
mainPanel(
fluidRow(conditionalPanel("output.rand == 1"),
tags$h4("Here comes the default part"),
br(),
textOutput("defaultCalc")),
fluidRow(conditionalPanel("output.randomint != 1",
tags$h4("I can evaluate if the chosen number is even or odd."),
br(),
textOutput("evenodd")
),
fluidRow(conditionalPanel("output.evenodd == 'Number is even'",
tags$h4("Number even calculation "),
textOutput("msLoc"),
br(),
textOutput("searchMW"),
br(),
textOutput("defaultID"),
br()
),
fluidRow(conditionalPanel("output.evenodd == 'Number is odd'",
tags$h4("Here is some id:", textOutput("id")),
textOutput("displayFull")
)
)
)
)
)))
#
server <- function(input, output) {
rand1 <- reactive({
if(is.null(input$rand)){return(NULL)}
rn <- input$rand
return(rn)
})
randomint <- reactive({
seedn <- rand1()
set.seed(seedn)
rint <- sample(1:50, 1)
return(rint)
})
calc1 <- reactive({
intn <- randomint()
modn <- intn %% 2
return(modn)
})
evenOdd <- reactive({
modn <- calc1()
if(modn == 0){valueText = "Number is even"}
if(modn != 0){valueText = "Number is odd"}
return(valueText)
})
idtext <- reactive({
idint <- sample(1:10000, 3)
idint <- as.character(idint)
idint <- paste(idint, collapse = "")
return(idint)
})
output$defaultCalc <- renderText({
as.character(randomint())
})
output$evenodd <- renderText({
evenOdd()
})
output$searchMW <- renderText({
searchMWText
})
output$defaultID <- renderText({
bid
})
output$id <- renderText({
idtext()
})
output$displayFull <- renderText({
fulltext
})
}
shinyApp(ui = ui, server = server)
The problem is, the parts after default always appear, e..g., 'Here is some id' text always appears and this is not what I want. I want to display 'Here is some id' and run the calculation (idtext) only when the number is odd.The number is not coming from the slider input, the slider input is providing the seed only. The number is also calculated and depends on its value, the other parts should be run and displayed. Until the user selects a slider input value, only the 'default part' should be displayed and nothing else.
I searched a lot and could not find a solution that mentions the conditions on output. What is the best way to solve this?
Do:
randomint <- reactive({
seedn <- rand1()
set.seed(seedn)
rint <- sample(1:50, 1)
return(rint)
})
output$randomint <- reactive(randomint())
outputOptions(output, "randomint", suspendWhenHidden = FALSE)
Then you can use "output.randomint !== 1".

Keep UI Text Input after adding or removing Inputs

I'm building a small UI where a user will enter into a splitLayout row of text that builds a statement (not needed for this question) to solve a puzzle.
However, if the user decides he/she needs an additional row or less rows to solve the puzzle I'd like adding or removing a new row of inputs to NOT delete the remaining input rows.
* the gray is a placeholder.
How can I best achieve my desired result of:
Please find my trimmed code below. Thanks for your input.
library(shiny)
# Define UI
ui <- fluidPage(
# Application title
titlePanel("Identify A, B and C"),
sidebarLayout(
sidebarPanel(width = 5,
helpText("Present a statement and receive a response: 1 is a Knight who always tells the truth, 2 is a Knave who always lies, and 3 is a Normal who can do either."),
# Number of Questions
numericInput(inputId = "Questions", label = "Number of Questions",
value = 1, min = 1, max = 10, step = 1),
splitLayout(cellWidths = c("25%","70%"),
style = "border: 1px solid silver;",
cellArgs = list(style = "padding: 3px"),
uiOutput("textQuestions"), uiOutput("textQuestions2"))
),
mainPanel(
# Right hand side output
)
)
)
# Define server logic
server <- function(input, output) {
####### I don't want these to delete initially everytime??
output$textQuestions <- renderUI({
Questions <- as.integer(input$Questions)
lapply(1:Questions, function(i) {
textInput(inputId = paste0("Who", i), label = paste0(i, ". Ask:"), placeholder = "A")
})
})
########
output$textQuestions2 <- renderUI({
Questions <- as.integer(input$Questions)
lapply(1:Questions, function(i) {
textInput(inputId = paste0("Q", i) , label = paste0("Logic:"),
value = "", placeholder = "A == 1 & (B != 2 | C == 3)")
})
})
######
}
# Run the application
shinyApp(ui = ui, server = server)
It looks like someone already gave you an answer using uiOutput+renderUI, so I'm going to go the other route: using insertUI and removeUI.
Instead of having a numeric input for "number of questions", I replaced it with a button for "add question" and one for "remove question". I have a variable keeping track of how many questions there are. Every time "add question" is pressed, we add one row. When "remove question" is pressed, we remove the last row.
Here's the code:
library(shiny)
# Define UI
ui <- fluidPage(
# Application title
titlePanel("Identify A, B and C"),
sidebarLayout(
sidebarPanel(
width = 5,
helpText("Present a statement and receive a response: 1 is a Knight who always tells the truth, 2 is a Knave who always lies, and 3 is a Normal who can do either."),
# Buttons to add/remove a question
actionButton("add", "Add question"),
actionButton("remove", "Remove question"),
div(id = "questions",
style = "border: 1px solid silver;")
),
mainPanel(
# Right hand side output
)
)
)
# Define server logic
server <- function(input, output) {
# Keep track of the number of questions
values <- reactiveValues(num_questions = 0)
# Add a question
observeEvent(input$add, ignoreNULL = FALSE, {
values$num_questions <- values$num_questions + 1
num <- values$num_questions
insertUI(
selector = "#questions", where = "beforeEnd",
splitLayout(
cellWidths = c("25%","70%"),
cellArgs = list(style = "padding: 3px"),
id = paste0("question", num),
textInput(inputId = paste0("Who", num),
label = paste0(num, ". Ask:"),
placeholder = "A"),
textInput(inputId = paste0("Q", num) ,
label = paste0("Logic:"),
placeholder = "A == 1 & (B != 2 | C == 3)")
)
)
})
# Remove a question
observeEvent(input$remove, {
num <- values$num_questions
# Don't let the user remove the very first question
if (num == 1) {
return()
}
removeUI(selector = paste0("#question", num))
values$num_questions <- values$num_questions - 1
})
}
# Run the application
shinyApp(ui = ui, server = server)
EDIT
OP requested a way to retrieve the user's input based on a question number. To do that:
Add the following to the UI
numericInput("question_num", "Show question number", 1),
textOutput("question")
Add the following to the server
get_question <- function(q) {
paste(
input[[paste0("Who", q)]],
":",
input[[paste0("Q", q)]]
)
}
output$question <- renderText({
get_question(input$question_num)
})
You could store it in a reactive value:
global <- reactiveValues(ask = c(), logic = c())
observe({
Questions <- as.integer(input$Questions)
lapply(1:Questions, function(i) {
inputVal <- input[[paste0("Who", i)]]
if(!is.null(inputVal)){
global$logic[i] <- inputVal
}
inputValQ <- input[[paste0("Q", i)]]
if(!is.null(inputValQ)){
global$ask[i] <- inputValQ
}
})
})
That would yield the following code for you example:
As a side effect the values would also be stored if a input was removed and then retaken.
library(shiny)
# Define UI
ui <- fluidPage(
# Application title
titlePanel("Identify A, B and C"),
sidebarLayout(
sidebarPanel(width = 5,
helpText("Present a statement and receive a response: 1 is a Knight who always tells the truth, 2 is a Knave who always lies, and 3 is a Normal who can do either."),
# Number of Questions
numericInput(inputId = "Questions", label = "Number of Questions",
value = 1, min = 1, max = 10, step = 1),
splitLayout(cellWidths = c("25%","70%"),
style = "border: 1px solid silver;",
cellArgs = list(style = "padding: 3px"),
uiOutput("textQuestions"), uiOutput("textQuestions2"))
),
mainPanel(
# Right hand side output
)
)
)
# Define server logic
server <- function(input, output) {
global <- reactiveValues(ask = c(), logic = c())
observe({
Questions <- as.integer(input$Questions)
lapply(1:Questions, function(i) {
inputVal <- input[[paste0("Who", i)]]
if(!is.null(inputVal)){
global$ask[i] <- inputVal
}
inputValQ <- input[[paste0("Q", i)]]
if(!is.null(inputValQ)){
global$logic[i] <- inputValQ
}
})
})
####### I don't want these to delete initially everytime??
output$textQuestions <- renderUI({
Questions <- as.integer(input$Questions)
lapply(1:Questions, function(i) {
textInput(inputId = paste0("Who", i), label = paste0(i, ". Ask:"), placeholder = "A", value = global$ask[i])
})
})
########
output$textQuestions2 <- renderUI({
Questions <- as.integer(input$Questions)
lapply(1:Questions, function(i) {
textInput(inputId = paste0("Q", i) , label = paste0("Logic:"), value = global$logic[i],
placeholder = "A == 1 & (B != 2 | C == 3)")
})
})
######
}
# Run the application
shinyApp(ui = ui, server = server)

Changing Numeric Inputs to 0 if Check Box is Unchecked

I was wondering if it was possible to set the value of a numeric input via updateNumericInput to be equal to 0 if a checkbox is not clicked. Below is how my code is set up at the moment to generate the check boxes and numeric inputs. I had to use loops to create a dynamic number of check boxes and inputs due to the nature of the app so I would really appreciate any help linking the two while keeping the functionality.
Server file:
shinyServer(function(input, output, session) {
output$inputs1 <- renderUI({
numSliders <- input$sources
lapply(1:numSliders, function(i) {
numericInput(
inputId = paste0('slider1', i),
label = df[i,2],
value = df[i,3]*(input$budget)/100)
})
})
output$checks1 <- renderUI({
numSliders <- input$sources
lapply(1:numSliders, function(i) {
checkboxInput(
inputId = paste0('check1', i),
label = df[i,2],
value = TRUE
)
})
})
}
UI:
shinyUI(fluidPage(fluidRow(
sidebarLayout(
sidebarPanel(
column(5,numericInput("budget", "Budget", value = 0),
uiOutput("checks1")),
column(5,uiOutput("inputs1"))),
mainPanel()
)
)
)
)
Please let me know if there is any sort of workaround for this.
Thanks in advance!
Since you can only generate a finite number of widgets the easiest way of creating an observer for each checkboxInput is to create a global variable, say, max_widgets which gives an upper bound on widgets. You then restrict the maximal value of numericInput which controls a number of widgets to max_widgets (so input$sources) and require within renderUIs that
req(numSliders > 0 & numSliders <= max_widgets)
(I would use validate and need to inform the user that the number of widgets has to be non negative and is bound to max_widgets but in my shiny version there is a bug and validate doesn't work as supposed.)
You then create observers for each checkboxInput on the server side:
lapply(1:max_widgets, function(i) {
observeEvent(input[[paste0('check', i)]], {
print(paste0("update of numeric", i))
updateNumericInput(session, inputId = paste0('numeric', i),
value = 0)
})
})
Note that this will create observers for all possible checkboxes (checkboxes may not even exist - shiny won't complain :) )
This may not be perfect but, as said, you will have only one observer for each checkbox.
If you dynamically generate observers in a following way (without a global variable max_widgets)
observe({
lapply(1:input$sources, function(i) {
observeEvent(input[[paste0('check', i)]], {
print(paste0("numeric", i, " = ", input[[paste0('numeric', i)]]))
updateNumericInput(session, inputId = paste0('numeric', i),
value = 0)
})
})
it will work too but each time you will generate new widgets you will also create an observer for it. So you may get multiple observers for each checkboxInput!
If your app is small then it won't matter much but in general it may lead to bugs. You can easy deal with it but it makes the code slightly more complicated - there was a question that touched on this problem.
Full example:
library(shiny)
rm(list = ls())
max_widgets <- 15
server <- shinyServer(function(input, output, session) {
output$inputs1 <- renderUI({
numSliders <- input$sources
# My shiny version has a bug and can't use validate(need(...)) because
# it doesn't work as suppossed
req(numSliders > 0 & numSliders <= max_widgets)
lapply(1:numSliders, function(i) {
numericInput(
inputId = paste0('numeric', i),
# label = df[i,2],
paste0("Input ", i),
# value = df[i,3] * (input$budget) / 100)
value = i * (input$budget) / 100)
})
})
output$checks1 <- renderUI({
numSliders <- input$sources
req(numSliders > 0 & numSliders <= max_widgets)
lapply(1:numSliders, function(i) {
list(
checkboxInput(
inputId = paste0('check', i),
# label = df[i,2],
label = paste0("Checkbox ", i),
value = TRUE
),
br()
)
})
})
lapply(1:max_widgets, function(i) {
observeEvent(input[[paste0('check', i)]], {
print(paste0("update of numeric", i))
updateNumericInput(session, inputId = paste0('numeric', i),
value = 0)
})
})
})
ui <- shinyUI(fluidPage(fluidRow(
sidebarLayout(
sidebarPanel(
column(5,
numericInput("budget", "Budget", value = 0),
hr(),
br(),
uiOutput("checks1")
),
column(5,
numericInput("sources", "Sources", value = 0, min = 0, max = max_widgets),
hr(),
uiOutput("inputs1")
)
),
mainPanel()
)
)))
shinyApp(ui, server)

Shiny R renderPrint in loop usinf RenderUI only update the output

I am trying to dynamically render multiple text output from multiple text input. I tried to use this very helpfull example and this one too.
This conversation is also helpfull.
But when I try to adapt this examples on the following script, I have a problem of output update. Apparently, only the last element was read and updated. It's probably a reactivity problem but it seems to be difficult to associate reactive{()} and renderUI{()}functions.
rm(list = ls())
library(shiny)
creatDataElem <- function(ne, input) {
x1 <- lapply(1:ne, function(i) {
textInput(paste0("elemName", i),
label = h4(strong("Name of dataset element")),
value = "")
})
return(x1)
}
ui = (fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("elemNb",
"Number of elements", value = 1, min = 1,
max = 3)
,
conditionalPanel(
condition = "input.elemNb == 1",
creatDataElem(1)
),
conditionalPanel(
condition = "input.elemNb == 2",
creatDataElem(2)
),
conditionalPanel(
condition = "input.elemNb == 3",
creatDataElem(3)
)
),
mainPanel(
uiOutput("nameElem")
)
)
)
)
server = function(input, output, session) {
max_elem <- 3
# Name
output$nameElem <-renderUI({
nameElem_output_list <- lapply(1:input$elemNb, function(i) {
elemName <- paste0("elemName", i)
tags$div(class = "group-output",
verbatimTextOutput(elemName)
)
})
do.call(tagList, nameElem_output_list)
})
for (i in 1:max_elem) {
local({
force(i)
my_i <- i
elemName <- paste0("elemName", my_i)
output[[elemName]] <- renderPrint(input[[elemName]])
})
}
}
runApp(list(ui = ui, server = server))
The idea with a reactive({}) function is to add an independant object (a function in this case) like:
nameElem <- reactive({
if (input$goElem == 0) {
return()
} else {
isolate({
if (is.null(input$elemName)) {
return()
} else if (test(input$elemName)) {
return("TEST RESULT")
} else {
return(input$elemName)
}
})
}
})
and to use renderUI on this object (with an ActionButton).
So, if someone knows why the output does not return the good object...
I think one of your problems is that your creatDataElem function is such that when it is called with argument ne=3, the first and second textInput elements are created again (and their value "lost").
Anyway, I think one solution would be to create those textInput elements as an "uiOutput".
You'll find a possible solution below which (I think) does what you want.
Lise
rm(list = ls())
library(shiny)
ui = (fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("elemNb",
"Number of elements", value = 1, min = 1,
max = 3),
uiOutput("myUI")
),
mainPanel(
uiOutput("nameElem")
)
)
)
)
server = function(input, output, session) {
output$myUI=renderUI({
w=""
for (i in 1:input$elemNb){
w=paste0(w,
textInput(paste0("elemName",i),label='Name of dataset element'))
}
HTML(w)
})
output$nameElem <-renderUI({
elems=c("<div>")
for(i in 1:input$elemNb){
elems=paste(elems,"</div><div>",input[[paste0("elemName",i)]])
}
elems=paste0(elems,"</div>")
HTML(elems)
})
}
runApp(list(ui = ui, server = server))
Found a solution:
library(readr)
library(dplyr)
library(shiny)
df <- data.frame(symbol = 1:10)
uiOutput("myUI")
createUI <- function(dfID, symbol) {
div(class="flex-box",paste0(symbol, " - 10"))
}
output$myUI <- renderUI({
w <- lapply(seq_len(nrow(df)), function(i) {
createUI(i, df[i,"symbol"])
})
do.call(fluidPage,w)
})

Resources