I am trying to filter the data in a datatable based from the inputs from the slider range. When this is done I get an error subscript out of bounds. I see the slider range to be working fine. But the range doesnt seems to filter the data table.
Below is the code which I have used :
response_codes <- function(status_code){
status_df <- tibble::tribble(
~status_code, ~message,
200, "Success",
201, "Successfully created item",
204, "Item deleted successfully",
400, "Something was wrong with the format of your request",
401, "Unauthorized - your API key is invalid",
403, "Forbidden - you do not have access to operate on the requested item(s)",
404, "Item not found",
429, "Request was throttled - you are sending too many requests too fast."
)
out <- status_df[status_df$status_code == status_code, "message"]
out <- unlist(out, use.names = FALSE)
out
}
install.packages("devtools")
library(tidyr)
lego_get <- function(url, ..., api_key){
auth <- paste("key", api_key)
query = list(...)
# Call the apiƄ
api_call <- httr::GET(url, query = query,
httr::add_headers(Authorization = auth))
if(httr::status_code(api_call) > 204){
stop(response_codes(httr::status_code(api_call)))
} else {
message(response_codes(httr::status_code(api_call)))
}
# Collect data
out <- list()
api_data <- httr::content(api_call)
if(is.null(api_data$results)){
api_data <- null_to_na(api_data)
return(api_data)
}
if(length(api_data$results) == 0){
api_data$results <- NA
api_data <- null_to_na(api_data)
return(api_data)
}
out <- c(out, list(api_data$results))
# While loop to deal with pagination
while(!is.null(api_data$`next`)){
message(paste("Pagenating to:", api_data$`next`))
api_call <- httr::GET(api_data$`next`, httr::add_headers(Authorization = auth))
api_data <- httr::content(api_call)
out <- c(out, list(api_data$results))
}
# Flatten the list
out <- purrr::flatten(out)
# Set nulls to NA
out <- null_to_na(out)
# Return data
out
}
null_to_na <- function(mylist){
purrr::map(mylist, function(x){
if(is.list(x)){
null_to_na(x)
} else {
if(is.null(x)) NA else x
}
})
}
color_list_to_df <- function(lego_data){
out <- purrr::map_df(lego_data, function(color){
external_ids <- names(color$external_ids)
col_df <- purrr::map_df(external_ids, function(external_id){
ext_ids <- unlist(color$external_ids[[external_id]]$ext_ids)
df <- tibble::tibble(
external_id = external_id,
ext_ids = ext_ids
)
ext_descrs <- color$external_ids[[external_id]]$ext_descrs
ext_descrs <- purrr::map(ext_descrs, unlist)
df$ext_descrs <- ext_descrs
df <- tidyr::unnest(df, ext_descrs)
df
})
external <- tidyr::nest(col_df, .key = external_ids)
tibble::tibble(
id = color$id,
name = color$name,
rgb = color$rgb,
is_trans = color$is_trans,
external_ids = external$external_ids
)
})
out
}
parts_list_to_df <- function(lego_data){
out <- purrr::map_df(lego_data, function(parts_data){
if(length(parts_data$external_ids) != 0){
part_df <- tibble::tibble(
external_ids = names(parts_data$external_ids)
)
part_df$ids <- purrr::map(part_df$external_ids, function(ext_name){
unlist(parts_data$external_ids[[ext_name]])
})
part_df <- tidyr::unnest(part_df, ids)
external <- tidyr::nest(part_df, .key = external_ids)
} else {
external <- list()
external$external_ids <- NA
}
tibble::tibble(
part_num = parts_data$part_num,
name = parts_data$name,
part_cat_id = parts_data$part_cat_id,
part_url = parts_data$part_url,
part_img_url = parts_data$part_img_url,
external_ids = external$external_ids
)
})
out
}
###############################################################
url <- "https://rebrickable.com/api/v3/lego/sets/"
api_key <- "5baf593383d5f6a7fadd264480287ac9"
lego_data <- lego_get(url = url, api_key = api_key)
message("Converting to tibble")
out <- purrr::map_df(lego_data, tibble::as_tibble)
out
###############################################################
#devtools::install_github("rstudio/shiny")
#install.packages("devtools")
#install.packages("DT")
library(shiny)
library(devtools)
library(DT)
library(yaml)
# Define UI for slider demo app ----
ui <- fluidPage(
# App title ----
titlePanel("Sliders"),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar to demonstrate various slider options ----
sidebarPanel(
# Input: Specification of range within an interval ----
sliderInput("range", "Range:",
min = min(out$year,na.rm=FALSE), max = max(out$year,na.rm=FALSE),
value = c(1990,1995))
),
mainPanel(
DT::dataTableOutput("mytable")
)
)
)
server <- function(input, output) {
# sorted columns are colored now because CSS are attached to them
# output$mytable <- DT::renderDataTable({
# DT::datatable(out, options = list(orderClasses = TRUE))
# })
minRowVal <- reactive({
which(grepl(input$range[[1]], out$year)) #Retrieve row number that matches selected range on sliderInput
})
maxRowVal <- reactive({
which(grepl(input$range[[2]], out$year)) #Retrieve row number that matches selected range on sliderInput
})
observeEvent(input$range, {
output$mytable <- DT::renderDataTable({
DT::datatable[minRowVal():maxRowVal(), ]
})
})
}
shinyApp(ui, server)
Update the code from where I fetch the data to run display it on the app.
There are two types of shiny slider bars and they can have either one or two values. The number of values in the slider bar will be determined by how it is defined in the ui.
Because you only defined a singular slider in the initiation of the ui, there is not a second input input$range[[2]] when you try to extract it later in the reactive. Therefore, you need to set a second value in your ui or you will only get a single slider instead of a range. For example:
sliderInput("range", "Range:",
min = min(out$year,na.rm=FALSE), max = max(out$year,na.rm=FALSE),
value = c(1990,1991))
For an example of the difference between the two (slider bar vs. slider range) look here
And note:
If value is a vector of two numbers, Shiny will place two sliders on the bar, which will let your user select the endpoints of a range. If value is a single number, Shiny will create a basic slider like the one shown above.
Related
I have a simple shiny app that holds a dataset as a reactive value.
Once a button is pressed, a function should be applied to each row and the result is added as another variable to that dataset.
The dataset is also shown as a DT.
The result variable should be rendered as soon as the computation for that row is finished.
At the moment, the loop/apply that applies the function to each row finishes and only afterwards the results are displayed.
As the function can run for a long time, I want the DT to be updated as soon as a run is finished, not when all runs finish.
I understand that this means I need to use promises/future so that the main shiny code block spawns new processes which do not block in this case the main thread from updating the values. Correct?
However, I am not able to get it to work.
Here is a small MWE using a simple for loop
library(shiny)
library(DT)
ui <- fluidPage(
actionButton("run", "RUN"),
hr(),
DT::dataTableOutput("table")
)
calc_fun <- function(val) {
Sys.sleep(0.5)
val * 10
}
server <- function(input, output, session) {
set.seed(123)
data_res <- reactiveVal(data.frame(id = 1:10, val = rnorm(10), val10 = NA))
observe({
for (i in seq(nrow(data_res()))) {
print(paste("Looking at row", i))
d <- data_res()
d[i, "val10"] <- calc_fun(val = d[i, "val"])
data_res(d)
}
}) %>% bindEvent(input$run)
# This should be rendered whenever a round in the for-loop has finished
# at the moment it is only run once the loop is finished
output$table <- DT::renderDataTable(data_res())
}
shinyApp(ui, server)
Thanks to #ismirsehregal, I came up with the following solution which uses futures to start the calculation in the background, which in turn write the current status to a file.
Shiny then reactively reads the file and updates the values.
The full MWE looks like this:
library(shiny)
library(DT)
library(future)
library(promises)
library(qs) # for fast file read/write, replace with csv if needed
plan(multisession)
ui <- fluidPage(
actionButton("run", "RUN"),
hr(),
textOutput("prog"),
uiOutput("status"),
hr(),
fluidRow(
column(6,
h2("Current Status"),
DT::dataTableOutput("table")
),
column(6,
h2("Data in File"),
tableOutput("file_data")
)
)
)
calc_fun <- function(val) {
Sys.sleep(runif(1, 0, 2))
val * 10
}
# main function that goes through the rows and starts the calculation
# note that the output is saved to a .qs file to be read in by another reactive
do_something_per_row <- function(df, outfile) {
out <- tibble(id = numeric(0), res = numeric(0))
for (i in seq(nrow(df))) {
v <- df$val[i]
out <- out %>% add_row(id = i, res = calc_fun(v))
qsave(out, outfile)
}
return(out)
}
# create a data frame of tasks
set.seed(123)
N <- 13
tasks_init <- tibble(id = seq(N), val = round(rnorm(N), 2), status = "Open", res = NA)
server <- function(input, output, session) {
# the temporary file to communicate over
outfile <- "temp_progress_watch.qs"
unlink(outfile)
data <- reactiveVal(tasks_init) # holds the current status of the tasks
data_final <- reactiveVal() # holds the results once all tasks are finished
output$prog <- renderText(sprintf("Progress: 0 of %i (0.00%%)", nrow(data())))
output$status <- renderUI(div(style = "color: black;", h3("Not yet started")))
# on the button, start the do_something_per_row function as a future
observeEvent(input$run, {
# if a file exists => the code runs already
if (file.exists(outfile)) return()
print("Starting to Run the code")
output$status <- renderUI(div(style = "color: orange;", h3("Working ...")))
d <- data()
future({do_something_per_row(d, outfile)}, seed = TRUE) %...>% data_final()
print("Done starting the code, runs now in the background! freeing the session for interaction")
# return(NULL) # hide future
})
observe({
req(data_final())
output$status <- renderUI(div(style = "color: green;", h3("Done")))
print("All Done - Results came back from the future!")
})
output$file_data <- renderTable(req(df_done()))
output$table <- DT::renderDataTable({
# no need to fire on every refresh, this is handled automatically later
DT::datatable(isolate(data())) %>%
formatStyle("status", color = styleEqual(c("Open", "Done"), c("white", "black")),
backgroundColor = styleEqual(c("Open", "Done"), c("red", "green")))
})
dt_proxy <- DT::dataTableProxy("table")
# look for changes in the file and load it
df_done <- reactiveFileReader(300, session, outfile, function(f) {
r <- try(qread(f), silent = TRUE)
if (inherits(r, "try-error")) return(NULL)
r
})
observe({
req(df_done())
open_ids <- data() %>% filter(status == "Open") %>% pull(id)
if (!any(df_done()$id %in% open_ids)) return()
print(paste("- new entry found:", paste(intersect(df_done()$id, open_ids), collapse = ", ")))
rr <- data() %>% select(-res) %>% left_join(df_done(), by = "id") %>%
mutate(status = ifelse(is.na(res), "Open", "Done"))
data(rr)
DT::replaceData(dt_proxy, rr)
# replace the progress text
txt <- sprintf("Progress: % 4i of % 4i (%05.2f%%)",
nrow(df_done()), nrow(data()), 100 * (nrow(df_done()) / nrow(data())))
output$prog <- renderText(txt)
})
}
shinyApp(ui, server)
or as a picture:
The below code runs fine so long as the line in server section v <- reactiveValues(results=tibble(Scenario = 1, data())) is commented out. When I uncomment it the App crashes and I get the error message: Warning: Error in : Can't access reactive value 'input1' outside of reactive consumer. i Do you need to wrap inside reactive() or observer()?
I'm trying to create a vector using tibble for another function to be added. What am I doing wrong here in my use of tibble? I'm trying to capture via tibble, as a vector, the values generated by my custom interpol function when it takes inputs from matrix input2 through the data() function below. I also tried c(), as.vector(), etc., to make sure input2 is converted to a vector but I still get the same error.
I'm completely new to tibbles and tidyverse etc.
Code:
library(shiny)
library(shinyMatrix)
library(tidyverse) # < ADDED
interpol <- function(a,b){ # a = periods, b = matrix inputs
c <- rep(NA,a)
c[1] <- b[1]
c[a] <- b[2]
c <- approx(seq_along(c)[!is.na(c)],c[!is.na(c)],seq_along(c))$y # this interpolates
return(c)
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(uiOutput("panel"),actionButton("showInput2","Modify/add interpolation")),
mainPanel(plotOutput("plot"))
)
)
server <- function(input, output, session){
data <- function(){
if(!isTruthy(input$input1)){interpol(6,matrix(c(1,5)))} else {
if(!isTruthy(input$input2)){interpol(input$periods,
matrix(c(input$input1[1,1],input$input1[1,2])))} else {
interpol(input$periods,matrix(c(input$input2[1,1],input$input2[1,2])))}}
}
# v <- reactiveValues(results=tibble(Scenario = 1, data()))
output$panel <- renderUI({
tagList(
sliderInput('periods','Interpolate over periods (X):',min=2,max=12,value=6),
uiOutput("input1"))
})
output$input1 <- renderUI({
matrixInput("input1",
label = "Interpolation 1 (Y values):",
value = matrix(if(isTruthy(input$input2)){c(input$input2[1],input$input2[2])}
else {c(1,5)}, # matrix values
1, 2, # matrix row/column count
dimnames = list(NULL,c("Start","End"))), # matrix column header
rows = list(names = FALSE), class = "numeric")
})
observeEvent(input$showInput2,{
showModal(
modalDialog(
matrixInput("input2",
label = "Automatically numbered scenarios (input into blank cells to add):",
value = if(isTruthy(input$input2)){input$input2}
else if(isTruthy(input$input1)){input$input1},
rows = list(names = FALSE),
cols = list(extend = TRUE, delta = 2, delete = TRUE, multiheader=TRUE),
class = "numeric"),
footer = modalButton("Close")
))
})
observe({
req(input$input2)
mm <- input$input2
colnames(mm) <- paste(trunc(1:ncol(mm)/2)+1, " (start|end)")
isolate(updateMatrixInput(session, "input2", mm))
})
output$plot<-renderPlot({plot(data(),type="l",xlab="Periods (X)", ylab="Interpolated Y values")})
}
shinyApp(ui, server)
The reason for the error and solution actually is present in the error message itself. You cannot access reactive variable (data()) outside reactive context. Try wrapping the v output in reactive.
v <- reactive({tibble(Scenario = 1, data())})
Suppose I have the following blocks of codes in Shiny:
library(shiny)
rv <- reactiveValues()
observe({
# Event A
# Code Block A
# The code below signals the end of Code Block A
rv$event_a <- F
rv$event_a <- T
})
observe({
# Event B
# Code Block B
# The code below signals the end of Code Block B
rv$event_b <- F
rv$event_b <- T
})
observe({
rv$event_a
rv$event_b
if(rv$event_a & rv$event_b) {
# Do something only after both Code Blocks A and B finish running.
# Code Block C
}
})
As you can see, I'm toggling the reactive values in Blocks A and B from FALSE to TRUE to trigger Block C to run.
I want to write the code so that the cycle can repeat itself:
Some trigger -> Block A & B -> C ->
Some trigger -> Block A & B -> C ...
My questions are the following:
How can I make Code Block C run only once, when both Code Block A and B finished running?
How else can I achieve triggering Code Block C without the weird toggling of reactive values (between FALSE and TRUE) that I am currently relying on?
I have accomplished this before by eventObserving or eventReacting to the reactive objects or reactiveValues generated by 'code-block-a' or 'code-block-b'. I have attached 3 small shiny app examples to give insight into this approach using different methods (hopefully these will help answer the original question - or at least give some ideas).
This app will create a table in 'code-block-a' with as many rows as the sliderInput has selected. Once this 'event_a()' reactive is updated 'code-block-b' subsets one row. Once 'code-block-b' updates its object 'event_b()' a modal is displayed showing the selected row in a table.
library(shiny)
library(tidyverse)
ui <- fluidPage(
sliderInput("slide", "slide", value = 5, min = 1, max = 10),
actionButton("go", "go"),
)
server <- function(input, output, session) {
rv <- reactiveValues(tr1 = 0, el = 0)
final <- reactiveValues()
#CODE BLOCK A#
#takes slider input and makes a table with that many rows
event_a <- eventReactive(input$go,{
nums <- seq(1, input$slide, by = 1)
l <- 1:length(nums)
tibble(Letter = letters[l],
Value = nums)
})
#trigger next series of events in response to event_a()
#observeEvent(event_a(),{
# rv$el <- rv$el + 1
# })
##CODE BLOCK B##
# this will subset a row of data based on the value of the reactive
event_b <- eventReactive(event_a(), {
row <- sample(1:nrow(event_a()), 1)
event_a()[row,]
})
#look for changes in event_b() to trigger event C
#the loading of event_b will trigger the modal via rv$tr1
# observeEvent(event_b(), {
# rv$tr1 <- rv$tr1 + 1
# })
#side effect make a table from event_b() to be shown in modal
output$modal_plot <- renderTable({
event_b()
})
##CODE BLOCK C##
#launch modal showing table
observeEvent(event_b(), {
showModal(modalDialog(title = "Table",
"This is a table",
tableOutput("modal_plot"),
inline = T))
})
}
shinyApp(ui, server)
Or if all your 'code-block' are observers you can use reactive values that are updated inside of an observer. I have found this flexible if multiple things need to happen to trigger something downstream:
library(shiny)
library(tidyverse)
ui <- fluidPage(
sliderInput("slide", "slide", value = 5, min = 1, max = 10),
actionButton("go", "go"),
)
server <- function(input, output, session) {
rv <- reactiveValues(tr1 = 0, el = 0)
final <- reactiveValues()
#CODE BLOCK A#
#takes slider input and makes a table with that many rows
event_a <- eventReactive(input$go,{
nums <- seq(1, input$slide, by = 1)
l <- 1:length(nums)
tibble(Letter = letters[l],
Value = nums)
})
#trigger next series of events in response to event_a()
observeEvent(event_a(),{
rv$el <- rv$el + 1
})
##CODE BLOCK B##
# this will subset a row of data based on the value of the reactive
event_b <- eventReactive(rv$el, ignoreInit = T, {
row <- sample(1:nrow(event_a()), 1)
event_a()[row,]
})
#look for changes in event_b() to trigger event C
#the loading of event_b will trigger the modal via rv$tr1
observeEvent(event_b(), {
rv$tr1 <- rv$tr1 + 1
})
#side effect make a table from event_b() to be shown in modal
output$modal_plot <- renderTable({
event_b()
})
##CODE BLOCK C##
#launch modal showing table
observeEvent(rv$tr1, ignoreInit = T, {
showModal(modalDialog(title = "Table",
"This is a table",
tableOutput("modal_plot"),
inline = T))
})
}
shinyApp(ui, server)
Furthermore, if you are wanting something that iterates like a loop here is an example that describes the above process, but plots each row of data in a modal one row at a time and asking for user input each time:
library(shiny)
library(tidyverse)
ui <- fluidPage(
sliderInput("slide", "slide", value = 5, min = 1, max = 10),
actionButton("go", "go"),
tableOutput("df"),
tableOutput("user_choices_table")
)
server <- function(input, output, session) {
rv <- reactiveValues(tr1 = 0, el = 0)
final <- reactiveValues()
#STEP 1
#some function/series of events that gives us a some data
data1 <- eventReactive(input$go,{
c <- seq(1, input$slide, by = 1)
l <- 1:length(c)
out_table <- tibble(Letter = letters[l],
Value = c)
return(out_table)
})
#side effect - return data1 to UI
output$df <- renderTable({
data1()
})
#number of max iterations we will go though (dependent number of rows in data1)
num_iterations <- reactive({
nrow(data1())
})
#trigger next series of events in response to data1()
#this will get us from 0 to 1 and another observer will be used to add
#all the way up to the max_iterations
observeEvent(data1(),{
rv$el <- rv$el + 1
})
#this ^ observer is like entering the loop on the first iteration
##STEP 2##
##start/continue the "disjointed-loop".
#Subset data1 into smaller piece we want based on rv$el
#this will be our 'i' equivalent in for(i in ...)
#data subset
data2 <- eventReactive(rv$el, ignoreInit = TRUE, {
data2 <- data1()[rv$el,]
return(data2)
})
#side effect make a plot based on data2 to be shown in modal
output$modal_plot <- renderPlot({
d <- data2()
ggplot()+
geom_col(data = d, aes(x = Letter, y = Value, fill = Letter))+
theme_linedraw()
})
#once we get our data2 subset ask the user via modal if this is what they want
#the loading of data2 will trigger the modal via rv$tr1
observeEvent(data2(), {
rv$tr1 <- rv$tr1 + 1
})
##STEP 3##
#launch modal showing plot and ask for user input
observeEvent(rv$tr1, ignoreInit = TRUE, {
showModal(modalDialog(title = "Make a Choice!",
"Is this a good selection?",
plotOutput("modal_plot"),
checkboxGroupInput("check", "Choose:",
choices = c("Yes" = "yes",
"No" = "no"),
inline = T),
footer = actionButton("modal_submit", "Submit")))
})
#when user closes modal the response is saveed to final[[character representing number of iteration]]
observeEvent(input$modal_submit, {
final[[as.character(rv$el)]] <- input$check
if(rv$el < num_iterations()){
rv$el <- rv$el + 1 #this retriggers step2 to go again
} else {
rv$done <- rv$done + 1
} #breaks the disjointed loop and trigger start of next reactions
})
#and the modal is closed
observeEvent(input$modal_submit, {
removeModal()
})
final_choice <- eventReactive(rv$done, ignoreInit = TRUE,{
enframe(isolate(reactiveValuesToList(final))) %>%
mutate(name = as.numeric(name),
value = unlist(value)) %>%
arrange(name)
})
output$user_choices_table <- renderTable({
final_choice()
})
}
shinyApp(ui, server)
I'd like to be able to use results of an input in the Ui part of my shiny app, to set the default value and the maximum of a numericInput.
Here is the "idea" of the ui part i'd like :
ui <- (
numericInput("n21","choose input1",min=0,max=100000,value=5107,step=1),
numericInput("n22","choose input2",min=0,max=2000,value=1480.3/40),
# here i'd like to define value and max with the result of inputs (n23)
numericInput(inputId="nb_rows","Number of rows to show",value=n23,min=1,max=n23)
tableOutput(outputId = "data")
)
And the server part :
server <- function(input,output,session){
....
RE <- reactive({
n21 <- input$n21
n22 <- input$n22
n23 <- n21%/%n22
return(head(data, n=input$nb_rows))
})
output$data <- renderTable({RE()})
}
Any suggestions?
You will need to use the observe function to change the numericinput that you want to change so we will do:
`server <- function(input,output,session){
....
RE <- reactive({
n21 <- input$n21
n22 <- input$n22
n23 <- n21%/%n22
return(n23)
})`
` observe({
x <- RE()
# Can use character(0) to remove all choices
if (is.null(x))
x <- character(0)
# Can also set the label and select items
updateNumericInput(session, "nb_rows",
label = "Number of rows to show",
value = x,
min = 1,
max = x
)
})`
And then you re-make the output table function
I hope it helps.
I have successfully updated UI dynamically through renderUI(). I have a long list of inputs to choose from. The check boxes are used to dynamically add numeric inputs. So, to implement this, I used lapply. However, I have used values of selected check boxes in checkboxgroup itself to populate IDs of the dynamically added numerical input instead of using paste(input, i) in lapply.
ui code snippet :
checkboxGroupInput(inputId = "checkboxgrp", label = "Select types",
choices = list("ELECTAPP","NB W $","PUR","MANUAL LTR","REDEMPTION","NB W TRANSFER","NB WOUT $","OUTPUT")),
...
fluidRow(column(12, verbatimTextOutput("value")))
...
uiOutput("numerics")
server code snippet :
renderUI({
numInputs <- length(input$checkboxgrp)
if(numInputs==0){
wellPanel("No transaction selected")
}
else{
lapply(1:numInputs, function(i){
x[i]=input$checkboxgrp[i]
list(numericInput(input$checkboxgrp[i], min = 0, label = input$checkboxgrp[i],
value= input[[x[i]]] ))
})
}
})
output$value <- renderPrint({
numInputs <- length(input$checkboxgrp)
lapply(1:numInputs, function(i){
print(input[[x[i]]]) ## ERROR
})
})
I have used input[[x[i]]] as to instantiate value to be retained after adding or removing a numeric input. But, I want to extract values from input$x[i] or input[[x[i]]] into a vector for further use which I'm unable to do.
*ERROR:Must use single string to index into reactivevalues
Any help is appreciated.
EDIT
using 3 different ways of extracting values from input generate 3 different errors:
Using print(input$x[i]) # ERROR
NULL
NULL
NULL
NULL
[[1]]
NULL
[[2]]
NULL
[[3]]
NULL
[[4]]
NULL
Using print(input[[x[i]]]) # ERROR
Must use single string to index into reactivevalues
Using print('$'(input, x[i])) # ERROR
invalid subscript type 'language'
If I understand you correctly, you want to access values of dynamically generated widgets and then just print them out.
In my example below, which should be easy to generalise, the choices are the levels of the variable Setosa from the iris dataset.
The IDs of the generated widgets are always given by the selected values in checkboxGroupInput. So, input$checkboxgrp says to shiny for which level of setosa there should be generated a widget. At the same time input$checkboxgrp gives IDs of generated widgets. That's why you don't need to store the IDs of "active" widgets in other variable x (which is probably a reactive value).
To print the values out you can do the following:
output$value <- renderPrint({
activeWidgets <- input$checkboxgrp
for (i in activeWidgets) {
print(paste0(i, " = ", input[[i]]))
}
})
This line print(input[[x[i]]]) ## ERROR yields an error because x[i] (whatever it is) is not a vector with a single value but with multiple values.
Full example:
library(shiny)
ui <- fluidPage(
titlePanel("Old Faithful Geyser Data"),
sidebarLayout(
sidebarPanel(
checkboxGroupInput("checkboxgrp", "levels", levels(iris$Species))
),
mainPanel(
fluidRow(
column(6, uiOutput("dynamic")),
column(6, verbatimTextOutput("value"))
)
)
)
)
server <- function(input, output) {
output$dynamic <- renderUI({
numInputs <- length(input$checkboxgrp)
if(numInputs==0){
wellPanel("No transaction selected")
}
else{
lapply(1:numInputs, function(i){
x[i]=input$checkboxgrp[i]
list(numericInput(input$checkboxgrp[i], min = 0, label = input$checkboxgrp[i],
value= input[[x[i]]] ))
})
}
})
output$value <- renderPrint({
activeWidgets <- input$checkboxgrp
for (i in activeWidgets) {
print(paste0(i, " = ", input[[i]]))
}
})
}
shinyApp(ui = ui, server = server)
Edit:
You could tweak the lapply part a little bit (mind <<- operator :) )
else{
activeWidgets <- input$checkboxgrp
val <- 0
lapply(activeWidgets, function(i){
val <<- val + 1
list(numericInput(i, min = 0, label = i,
value = val ))
})
}
Edit 2 In response to a comment:
server <- function(input, output) {
output$dynamic <- renderUI({
numInputs <- length(input$checkboxgrp)
if(numInputs==0){
wellPanel("No transaction selected")
}
else{
activeWidgets <- input$checkboxgrp
val <- 0
lapply(activeWidgets, function(i){
val <<- val + 1
list(numericInput(i, min = 0, label = i,
value = val ))
})
}
})
allChoices <- reactive({
# Require that all input$checkboxgrp and
# the last generated numericInput are available.
# (If the last generated numericInput is available (is not NULL),
# then all previous are available too)
# "eval(parse(text = paste0("input$", input$checkboxgrp))))" yields
# a value of the last generated numericInput.
# In this way we avoid multiple re-evaulation of allChoices()
# and errors
req(input$checkboxgrp, eval(parse(text = paste0("input$", input$checkboxgrp))))
activeWidgets <- input$checkboxgrp
res <- numeric(length(activeWidgets))
names(res) <- activeWidgets
for (i in activeWidgets) {
res[i] <- input[[i]]
}
res
})
output$value <- renderPrint({
print(allChoices())
})
}