I cant get a shiny module to work as a server . Only works when the server is separated as a separate set of commands - r

I have a shiny module and I'm having a huge issue getting it to work. I'm trying to create a dashboard with multiple tabs and am exploring modules to reduce the amount of duplication.
I can get the application to work if I hardcode the server explicitly with the code but when I create modules for the server part it doesn't won't work. I would really appreciate any help as I have tried looking everywhere for a workable example, Below is a reproducible example of a proportion of the code that I would like to modulize,
datasetInput <- function(id, Taxhead = NULL) {
ns <- NS(id)
names <- colnames(mtcars)
if (!is.null(Taxhead)) {
pattern <- paste0(Taxhead)
names <-names$name[sapply(names, function(x){ grepl(pattern,x, ignore.case = TRUE)})] #### filter for a match
}
selectInput(ns("dataset"), "Pick a Report", choices = names)
}
#### Server 1
#### Collect the data set based on the selection in datasetInput
datasetServer <- function(id) {
moduleServer(id, function(input, output, session) {
#### Outputs the data set
#### reactive( read.csv(paste0("Data/",input$dataset,".csv")) )
reactive( mtcars )
})}
#### Display the variables of interest
selectVarInput <- function(id){
ns <- NS(id)
tagList(
selectInput(ns("var"), "Select grouping Variables", choices = NULL, multiple = TRUE) ,
selectInput(ns("var2"), "Select Measure Variables", choices = NULL, multiple = TRUE)
) }
##### Server 2
#### Returns the data as a reactive
selectVarServer <- function(id, data) {
find_vars <- function(data, filter) { names(data)}
moduleServer(id, function(input, output, session) {
observeEvent(data(), {
updateSelectInput(session, "var", choices = find_vars(data()))
})
observeEvent(data(), {
updateSelectInput(session, "var2", choices = find_vars(data()))
})
reactive(data() %>% group_by(across(all_of(input$var))) %>% summarise(across(all_of(input$var2),sum), n = n()))
})}
selectDataVarUI <- function(id, Taxhead =NULL) {
ns <- NS(id)
tagList(
datasetInput(ns("data"), Taxhead ),
selectVarInput(ns("var"))
)}
#### Server 3
selectDataVarServer <- function(id) {
moduleServer(id, function(input, output, session) {
data <- datasetServer("data")
var <- selectVarServer("var", data)
var })}
Date_Range_UI <- function(id) {
ns <- NS(id)
# Sidebar to demonstrate various slider options ----
tagList(
# Sidebar with a slider input
# # Select form input for checking
radioButtons(ns("Period"),
label = "Select Desired Comparison Period",
choices = c( "Daily", "Monthly","Yearly"),
selected = "Monthly")
,
# Only show this panel if Monthly or Quarterly is selected
conditionalPanel(
condition = "input.Period != 'Yearly'", ns = ns,
dateRangeInput(ns('dateRange'),
label = 'Date range input',
start = Sys.Date()-180,
end = Sys.Date() ,
min = NULL, max = Sys.Date() ,
separator = " - ", format = "MM-yyyy",
startview = 'year', language = 'en', weekstart = 0,autoclose = TRUE))
,
# Only show this panel if Custom is selected
conditionalPanel(
condition = "input.Period == 'Yearly'", ns = ns,
sliderInput(ns("yearly"), "Years", min = 2000, max = as.integer(format(Sys.Date(),"%Y")), value = c(2008,2021), round = TRUE,step = 1)),
) ### close side bar layout
### close fluid page layout
}
Date_Range_Server <- function(id ) {
moduleServer(id,
function(input, output, session) {
x <- reactive({input$Period})
return(
list(
Startdate = reactive(if(x() == "Yearly") {input$yearly[1]
}
else if(x() == "Monthly") {
as.integer(format(input$dateRange[1],"%Y%m"))
}else{
as.integer(format(input$dateRange[1],"%Y%m%d"))})
,
Enddate = reactive(if(x() == "Yearly") {input$yearly[2]
}
else if(x() == "Monthly") {
as.integer(format(input$dateRange[2],"%Y%m"))
}else{
as.integer(format(input$dateRange[2],"%Y%m%d"))})
,
Choice = reactive(input$Period )))
})}
###### this won't work!
betting_UI <- function(id) {
ns <- NS(id)
sidebarLayout(
sidebarPanel(
Date_Range_UI("data_range"),
selectDataVarUI(id = "var", Taxhead =NULL)),
mainPanel(
tableOutput(ns("table")),
verbatimTextOutput (ns("test"))
)) }
Betting_Server <- function(input, output, session) {
date_range <- Date_Range_Server("data_range")
output$test <- renderPrint( date_range$Startdate())
output$table <- renderTable(var(), width = 40)
}
ui <- fluidPage(
betting_UI("betting")
)
server <- function(input, output, session) {
Betting_Server("betting")
}
shinyApp(ui, server)**
##### this works fine I thought putting the modules into the server would work as above?????
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
Date_Range_UI("data_range"),
selectDataVarUI(id = "var", Taxhead =NULL)),
mainPanel(
tableOutput("table"),
verbatimTextOutput ("test")
)) )
#### Server
server <- function(input, output, session) {
var <- selectDataVarServer("var")
date_range <- Date_Range_Server("data_range")
output$test <- renderPrint( date_range$Startdate())
output$table <- renderTable(var(), width = 40)
}
shinyApp(ui, server)

You have to use ns() in your module UI
betting_UI <- function(id) {
ns <- NS(id)
sidebarLayout(
sidebarPanel(
Date_Range_UI(ns("data_range")),
selectDataVarUI(id = ns("var"), Taxhead = NULL)
),
mainPanel(tableOutput(ns("table")),
verbatimTextOutput (ns("test")))
)
}
You also have to use moduleServer() to create the module server
Betting_Server <- function(id) {
moduleServer(id,
function(input, output, session) {
var <- selectDataVarServer("var")
date_range <- Date_Range_Server("data_range")
output$test <- renderPrint(date_range$Startdate())
output$table <- renderTable(var(), width = 40)
})
}

Related

How to dynamically update dropdown within a modulized shinyalert for each iteration of a for loop, when using html = TRUE?

I'm creating a shiny module, where I wish to display some pop-up messages to the user via shinyalerts and include dropdown menus via htlm = TRUE and shinyWidgets::pickerInput. For each shinyalert the options should be different and the alerts should appear right after each other when the user has selected the relevant option.
However, when running the shinyalerts within a for loop, only the first alert shows the drop-down, the following does not. Please have a look at the example below and screenshots. Any ideas what I'm doing wrong?
Module UI:
mod_match_columns_ui <- function(id){
ns <- NS(id)
tagList(
shinyalert::useShinyalert(),
actionButton(ns("run"), label = "Start!")
)
}
Module server:
mod_match_columns_server <- function(input, output, session){
ns <- session$ns
options <- list(c("option_1","option_2"),
c("option_3","option_4"))
observeEvent(input$run, {
for(col in 1:2){
nms <- options[[i]]
output[[paste0("dropdown",col)]] <- renderUI({
shinyWidgets::pickerInput(
inputId = ns(paste0("options",col)),
label = "Options listed below",
choices = nms,
selected = "",
multiple = FALSE,
options = shinyWidgets::pickerOptions(size = 15)
)
})
shinyalert::shinyalert(
title = "Pick an option!",
html = TRUE,
text = tagList(
uiOutput(ns(paste0("dropdown", col)))
),
inputId = ns(paste0("modal", col))
)
}
})
}
Run module:
library(shiny)
ui <- fluidPage(
mod_match_columns_ui("match_columns_ui_1")
)
server <- function(input, output, session) {
callModule(mod_match_columns_server, "match_columns_ui_1")
}
shinyApp(ui = ui, server = server)
First iteration:
Second iteration:
Why is the dropdown not shown in the second iteration?? Thanks
Try this
library(shiny)
library(shinyalert)
mod_match_columns_ui <- function(id){
ns <- NS(id)
tagList(
shinyalert::useShinyalert(),
actionButton(ns("run"), label = "Start!")
)
}
mod_match_columns_server <- function(id) {
moduleServer(id,
function(input, output, session) {
ns <- session$ns
options <- list(c("option_1","option_2"),
c("option_3","option_4"))
lapply(1:2, function(col){
output[[paste0("dropdown",col)]] <- renderUI({
shinyWidgets::pickerInput(
inputId = ns(paste0("options",col)),
label = paste("Options",col,"listed below"),
choices = options[[col]],
selected = "",
multiple = FALSE,
options = shinyWidgets::pickerOptions(size = 15)
)
})
})
observeEvent(input$run, {
shinyalert::shinyalert(
title = "Pick an option!",
html = TRUE,
text = tagList(
lapply(1:2, function(i){uiOutput(ns(paste0("dropdown",i)))})
)
# callbackR = function(x) { message("Hello ", x) },
# inputId = ns(paste0("modal"))
)
})
observe({
print(input$options1)
print(input$options2)
print(input$shinyalert)
})
})
}
ui <- fluidPage(
tagList(
mod_match_columns_ui("match_columns_ui_1")
)
)
server <- function(input, output, session) {
mod_match_columns_server("match_columns_ui_1")
}
shinyApp(ui = ui, server = server)

can't communicate data between shiny modules

I am trying to build a shiny App that uses several modules which communicate between them and share data. I have tried to create a simpler example that could be replicated to show the problem I'm facing.
The first module allows the user to select a dataset and a column from the selected dataset and then display the column in a table. The server part of the first module returns a list of statistics about the selected column (min,mean, max and sd).
The idea is to use these statistics to display them in a second module which creates textOutputs. The problem is that there is no reactivity in the app. Even when changing the dataset and columns the values in the textOutputs is the same.
### Module 1
mod_selectVar_ui <- function(id){
ns <- NS(id)
tagList(
selectInput(ns("dataset"), "Choose a dataset:",choices = c("rock", "pressure", "cars")),
selectInput(ns("colonnes"),label = "Choose some columns", choices = NULL, multiple = FALSE),
tableOutput(ns("table"))
)
}
#'
#'
mod_selectVar_server <- function(id){
moduleServer(id, function(input, output, session){
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
observe({
colonnes <- names(datasetInput())
updateSelectInput( session, "colonnes", choices = colonnes)
})
data <- reactive({
req(input$colonnes)
datasetInput()[, input$colonnes]
})
output$table <- renderTable({
head(data())
})
values <- reactive({
list(
meanVar = mean(data()),
maxVar = max(data()),
minVar = min(data()),
sdVar = sd(data())
)
})
return(values)
})
}
### Module 2
mod_textOu_ui <- function(id){
ns <- shiny::NS(id)
shiny::tagList(
shiny::textOutput(ns("txt"))
)
}
mod_textOu_server <- function(id, texte){
moduleServer(id,
function(input, output, session){
output$txt <- renderText({
texte
})
}
)
}
### Main App
ui <- fluidPage(
fluidRow(
column(3,
mod_textOu_ui("1")
),
column(3,
mod_textOu_ui("2")
),
column(3,
mod_textOu_ui("3")
),
column(3,
mod_textOu_ui("4")
)
),
fluidRow(
mod_selectVar_ui("1")
)
)
server <- function(input, output, session){
values <- mod_selectVar_server("1")
mod_textOu_server("1",values()$meanVar)
mod_textOu_server("2",values()$maxVar)
mod_textOu_server("3",values()$minVar)
mod_textOu_server("4",values()$sdVar)
}
shinyApp(ui ,server )
You have a duplicated ID 1 for you modules mod_selectVar_server("1") and mod_textOu_server("1",values()$meanVar). All IDs must be unique and using a number is not recommended.
Like #Limey said, you can't directly access the reactive value directly on the top level of your server. Reactive values must be accessed inside a reactive context. Pass the reactive directly to the function and access its value later inside your module.
When you change dataset, data will be invalid and it needs to wait for column names to update, so I added req(all(input$colonnes %in% names(datasetInput()))) to prevent the ugly red warnings that will briefly show up.
### Module 1
mod_selectVar_ui <- function(id){
ns <- NS(id)
tagList(
selectInput(ns("dataset"), "Choose a dataset:",choices = c("rock", "pressure", "cars")),
selectInput(ns("colonnes"),label = "Choose some columns", choices = NULL, multiple = FALSE),
tableOutput(ns("table"))
)
}
#'
#'
mod_selectVar_server <- function(id){
moduleServer(id, function(input, output, session){
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
observe({
colonnes <- names(datasetInput())
updateSelectInput(session, "colonnes", choices = colonnes)
})
data <- reactive({
req(input$colonnes)
req(all(input$colonnes %in% names(datasetInput())))
datasetInput()[, input$colonnes]
})
output$table <- renderTable({
head(data())
})
values <- reactive({
list(
meanVar = mean(data()),
maxVar = max(data()),
minVar = min(data()),
sdVar = sd(data())
)
})
return(values)
})
}
### Module 2
mod_textOu_ui <- function(id){
ns <- shiny::NS(id)
shiny::tagList(
shiny::textOutput(ns("txt"))
)
}
mod_textOu_server <- function(id, texte, item){
moduleServer(id,
function(input, output, session){
output$txt <- renderText({
texte()[[item]]
})
}
)
}
### Main App
ui <- fluidPage(
fluidRow(
column(3,
mod_textOu_ui("m1")
),
column(3,
mod_textOu_ui("m2")
),
column(3,
mod_textOu_ui("m3")
),
column(3,
mod_textOu_ui("m4")
)
),
fluidRow(
mod_selectVar_ui("s1")
)
)
server <- function(input, output, session){
values <- mod_selectVar_server("s1")
mod_textOu_server("m1",values, "meanVar")
mod_textOu_server("m2",values, "maxVar")
mod_textOu_server("m3",values, "minVar")
mod_textOu_server("m4",values, "sdVar")
}
shinyApp(ui ,server )

Using Shiny's updateSelectInput within nested modules

Background
The application is of the following structure:
.
├── R
│ ├── mod_observationSelector.R
│ ├── mod_previewTable.R
│ └── mod_summaryTable.R
└── app.R
With the files fulling the respective functions:
mod_observationSelector.R - provides an updateSelectInput mechanism facilitating selction of integere or real columns in mtcars data
mod_previewTable.R - generates head for selected column
mod_summaryTable.R - generates summary for selected column
Design assumptions
mod_observationSelector.R linked interface elements available in this module should be usable across remaining modules providing a selection mechanism
Problem
After nesting, the drop-down selection does no longer update.
Working version
Prior to nesting.
mod_observationSelector.R
observationSelectorUI <- function(id) {
ns <- NS(id)
fluidPage(
selectInput(
inputId = ns("varTypes"),
label = h3("Variable types"),
choices = list("Integer" = TRUE,
"Real" = FALSE),
selectize = FALSE,
multiple = FALSE
),
selectInput(
inputId = ns("selectColumn"),
label = h4("Selected Column"),
choices = character(0)
)
)
}
observationSelectorServer <- function(id, data) {
moduleServer(id,
function(input, output, session) {
observeEvent(eventExpr = input$varTypes,
handlerExpr = {
all_cols <- map_lgl(.x = mtcars, ~ all(. %% 1 == 0))
selected_cols <-
names(all_cols[all_cols == input$varTypes])
updateSelectInput(
session = session,
inputId = "selectColumn",
label = paste(
"Selected",
ifelse(input$varTypes, "integer", "real"),
"columns"
),
choices = selected_cols
)
})
})
}
app.R
library("shiny")
library("tidyverse")
ui <- fluidPage(
titlePanel("Nested Modules"),
observationSelectorUI("colChooser")
)
# Define server logic required to draw a histogram
server <- function(input, output) {
observationSelectorServer("colChooser")
}
# Run the application
shinyApp(ui = ui, server = server)
Broken version
Problems
Previously working updateSelect is now broken
app.R
library("shiny")
library("tidyverse")
ui <- fluidPage(titlePanel("Nested Modules"),
tabsetPanel(summaryUI("modSummary"),
previewUI("modPreview")
))
# Define server logic required to draw a histogram
server <- function(input, output) {
summaryServer("modSummary")
previewServer("modPreview")
}
# Run the application
shinyApp(ui = ui, server = server)
mod_observationSelector.R
In effect, no change.
observationSelectorUI <- function(id) {
ns <- NS(id)
fluidPage(
selectInput(
inputId = ns("varTypes"),
label = h3("Variable types"),
choices = list("Integer" = TRUE,
"Real" = FALSE),
selectize = FALSE,
multiple = FALSE
),
selectInput(
inputId = ns("selectColumn"),
label = h4("Selected Column"),
choices = character(0)
)
)
}
observationSelectorServer <- function(id, data) {
moduleServer(id,
function(input, output, session) {
observeEvent(eventExpr = input$varTypes,
handlerExpr = {
all_cols <- map_lgl(.x = mtcars, ~ all(. %% 1 == 0))
selected_cols <-
names(all_cols[all_cols == input$varTypes])
updateSelectInput(
session = session,
inputId = "selectColumn",
label = paste(
"Selected",
ifelse(input$varTypes, "integer", "real"),
"columns"
),
choices = selected_cols
)
})
})
}
mod_summaryTable.R
summaryUI <- function(id) {
ns <- NS(id)
tabPanel("Summary table",
column(4, observationSelectorUI(ns("colChooser"))),
column(8, tableOutput(ns('summaryTable'))))
}
summaryServer <- function(id) {
moduleServer(id,
function(input, output, session) {
output$summaryTable <-
renderTable(summary(mtcars[, input$selectColumn]))
})
}
mod_previewTable
previewUI <- function(id) {
ns <- NS(id)
tabPanel("Summary table",
column(4, observationSelectorUI(ns("colChooser"))),
column(8, tableOutput(ns('headTable'))))
}
previewServer <- function(id) {
moduleServer(id,
function(input, output, session) {
output$headTable <-
renderTable(head(mtcars[, input$selectColumn]))
})
}
Desired outcomes
Drop-down selection updates across the modules
Results from the in-module drop-down selection can be used in "outer" module to produce summaries, etc.
For convenience, the code is also available on GitHub: konradzdeb/nestedModule.
For posterity, the solution is as follows
mod_observationSelector.R
Reactive element is returned.
observationSelectorUI <- function(id) {
ns <- NS(id)
tagList(
selectInput(
inputId = ns("varTypes"),
label = h3("Variable types"),
choices = list("Integer" = TRUE,
"Real" = FALSE),
selectize = FALSE,
multiple = FALSE
),
selectInput(
inputId = ns("selectColumn"),
label = h4("Selected Column"),
choices = c("cyl", "hp", "vs", "am", "gear", "carb")
)
)
}
observationSelectorServer <- function(id, data) {
moduleServer(id,
function(input, output, session) {
observeEvent(eventExpr = input$varTypes,
handlerExpr = {
all_cols <- map_lgl(.x = mtcars, ~ all(. %% 1 == 0))
selected_cols <-
names(all_cols[all_cols == input$varTypes])
updateSelectInput(
session = session,
inputId = "selectColumn",
label = paste(
"Selected",
ifelse(input$varTypes, "integer", "real"),
"columns"
),
choices = selected_cols
)
})
# Return the selection result
return(reactive({
validate(need(input$selectColumn, FALSE))
input$selectColumn
}))
})
}
Using module inputs
As with any other reactive, I'm bringing the results from the nested module and then call them innerResult().
previewUI <- function(id) {
ns <- NS(id)
tabPanel("Summary table",
column(4, observationSelectorUI(ns("colChooser"))),
column(8, tableOutput(ns('headTable'))))
}
previewServer <- function(id) {
moduleServer(id,
function(input, output, session) {
innerResult <- observationSelectorServer("colChooser")
output$headTable <- renderTable(head(mtcars[, innerResult()]))
})
}
Full app
Available on GitHub: b25758b.

Using data from one shiny module to another shiny module

I am trying to use a value from one shiny module and pass it to a second shiny module to print it. So when user select orange from first dropdown it show print you have selected orange. But as of now it prints you have selected ATC which is nothing but the id I am passing . Below is the code I am using.Thank you.
library(shiny)
library(shinydashboard)
library(shinyWidgets)
dropDownUI <- function(id, div_width = "col-xs-12 col-md-8") {
ns <- NS(id)
div(column(3, uiOutput(ns("class_level"))),
column(3,uiOutput(ns("selected_product_ui"))
))
}
chartTableBoxUI <- function(id, div_width = "col-xs-12 col-md-8") {
ns <- NS(id)
div(tabBox(width = 12, title = id,
tabPanel(icon("bar-chart"),
textOutput(ns("selected_var")))
)
)
}
chartTableBox <- function(input, output, session, data,ImProxy) {
output$selected_var <- renderText({
ns <- session$ns
paste("You have selected",ns(ImProxy$selected_class))
})
}
dropDown <- function(input, output, session) {
ns <- session$ns
observe({output$class_level <- renderUI({
selectInput(
ns("selected_class"),
label = h4("Classification Level"),
choices = list(
"apple " = "apple",
"orange " = "orange"),
selected = "orange"
)})
})
a<-reactive({input$selected_class})
output$selected_product_ui <- renderUI({
req(input$selected_class)
Sys.sleep(0.2)
ns <- session$ns
if (input$selected_class == "apple") {
my_choices <- c("foo","zoo","boo")
} else if (input$selected_class == "orange") {
my_choices <- c("22","33","44")
} else {
my_choices <- c("aa","bb","cc")
}
selectInput(inputId = ns("selected_product"),
label = h4("Product Family"),
choices = my_choices)
})
}
sidebar <- dashboardSidebar(sidebarMenu(
menuItem("aaa",tabName = "aaa"),
menuItem("bbb", tabName = "bbb"),
menuItem("ccc", tabName = "ccc")
))
body <- ## Body content
dashboardBody(tabItems(
tabItem(tabName = "aaa",
fluidRow(dropDownUI(id = "dropdown"),
fluidRow(chartTableBoxUI(id = "ATC"))
)
)))
# Put them together into a dashboardPage
ui <- dashboardPage(
dashboardHeader(title = "Loyalty Monthly Scorecard"),
sidebar,
body
)
server = {
shinyServer(function(input, output, session) {
callModule(dropDown, id = "dropdown")
callModule(chartTableBox, id = "ATC", data = MyData)
})
}
shinyApp(ui = ui, server = server)
I tried the solution from this question Passing data within Shiny Modules from Module 1 to Module 2 using reactive values and observer event aargument "ImProxy" is missing, with no default
There are two issues with your code:
ImProxy is a user defined variable. You have not defined it, nor have you passed it as an argument.
You are using the id as the title of your tabBox.
Both are corrected below.
library(shiny)
library(shinydashboard)
library(shinyWidgets)
dropDownUI <- function(id, div_width = "col-xs-12 col-md-8") {
ns <- NS(id)
div(column(3,uiOutput(ns("class_level"))),
column(3,uiOutput(ns("selected_product_ui"))
))
}
chartTableBoxUI <- function(id, div_width = "col-xs-12 col-md-8") {
ns <- NS(id)
div(tabBox(width = 12, title = textOutput(ns("title_var")), ## fixing issue 2
tabPanel(icon("bar-chart"),
textOutput(ns("selected_var")))
)
)
}
chartTableBox <- function(input, output, session, data,a) { ## fixing issue 1
output$selected_var <- renderText({
paste("You have selected",a())
})
output$title_var <- renderText({ a() }) ## fixing issue 2
}
dropDown <- function(input, output, session) {
ns <- session$ns
observe({output$class_level <- renderUI({
selectInput(
ns("selected_class"),
label = h4("Classification Level"),
choices = list(
"apple " = "apple",
"orange " = "orange"),
selected = "orange"
)})
})
a<-reactive({input$selected_class})
output$selected_product_ui <- renderUI({
req(input$selected_class)
Sys.sleep(0.2)
ns <- session$ns
if (input$selected_class == "apple") {
my_choices <- c("foo","zoo","boo")
} else if (input$selected_class == "orange") {
my_choices <- c("22","33","44")
} else {
my_choices <- c("aa","bb","cc")
}
selectInput(inputId = ns("selected_product"),
label = h4("Product Family"),
choices = my_choices)
})
return(a) ## fixing issue 1
}
# Put them together into a dashboardPage
ui = dashboardPage(
dashboardHeader(title = "Loyalty Monthly Scorecard"),
dashboardSidebar(sidebarMenu(
menuItem("aaa",tabName = "aaa")
)),
dashboardBody(tabItems(
tabItem(tabName = "aaa",
fluidRow(dropDownUI(id = "dropdown"),
chartTableBoxUI(id = "ATC") # this text
)
)))
)
server = {
shinyServer(function(input, output, session) {
a = callModule(dropDown, id = "dropdown")
callModule(chartTableBox, id = "ATC", data = MyData, a = a)
})
}
shinyApp(ui = ui, server = server)

Access a dynamically generated input in r shiny

I have an app where the user needs to assign randomly generated elements (in this case, letters) to groups, but gets to decide how many groups to use. Because the selectInput where memberships are defined is generated dynamically in response to a number specified by the user, naming the menu is done automatically (e.g., usergroup1, usergroup2, etc.). I am having trouble accessing the input values and returning them from the module to use later because I won't know in advance how many inputs there will be, and hence how many usergroups to call. Here is an example app:
UI module:
library(shiny)
library(stringr)
mod1UI <- function(id) {
ns <- NS(id)
tagList(
numericInput(ns("n"), "N",value = NULL),
actionButton(ns("draw"),"Generate Letters"),
hr(),
numericInput(ns("groups"), "Enter number of groups (1-3)", value=NULL),
uiOutput(ns("groupings"))
)
}
What I tried to do here is make a list of usergroup names and return those, but the values aren't attached, and nothing comes through.
Server module:
mod1 <- function(input, output, session, data) {
ns <- session$ns
x <- reactiveValues(data=NULL)
observeEvent(input$draw, {
req(input$n)
x$data <- sample(letters,input$n)
})
output$groupings <- renderUI({
req(input$groups)
ltrs <- data()
lapply(1:input$groups, function(i) {
selectizeInput(paste0(session$ns("usergroup"),i),
paste0("Select letters for Group ", i),
choices=ltrs,
options = list(placeholder = "Select letters for this group",
onInitialize = I('function() { this.setValue(""); }')), multiple=T)
})
})
gps <- reactiveValues(gps=NULL)
reactive({
gps$gps <- lapply(1:input$groups, function(i) { paste0(session$ns("usergroup"),i) })
})
return(list(dat = reactive({x$data}),
groups = reactive({gps$gps})
))
}
UI:
ui <- navbarPage("Fancy Title",id = "tabs",
tabPanel("Panel1",
sidebarPanel(
mod1UI("input1")
),
mainPanel(verbatimTextOutput("lettersy")
)
)
)
Server:
server <- function(input, output, session) {
y <- callModule(mod1, "input1", data=y$dat)
output$lettersy <- renderText({
as.character(c(y$dat(), y$groups(), "end"))
})
}
shinyApp(ui, server)
Any help is greatly appreciated!
This solution mimics a couple others found on SO, namely this one.
The key is to create a reactiveValues object and then assign the values using [[i]]. In my case it helped to use a submit button to trigger that.
Complete, working code is as follows:
UI module:
library(shiny)
mod1UI <- function(id) {
ns <- NS(id)
tagList(
numericInput(ns("n"), "N",value = NULL),
actionButton(ns("draw"),"Generate Letters"),
hr(),
numericInput(ns("groups"), "Enter number of groups (1-3)", value=NULL),
uiOutput(ns("groupings")),
actionButton(ns("submit"), "Submit Groupings")
)
}
Server Module:
mod1 <- function(input, output, session, data) {
ns <- session$ns
x <- reactiveValues(data=NULL)
observeEvent(input$draw, {
req(input$n)
x$data <- sample(letters,input$n)
})
output$groupings <- renderUI({
req(input$groups)
ltrs <- data()
lapply(1:input$groups, function(i) {
selectizeInput(paste0(session$ns("usergroup"),i),
paste0("Select letters for Group ", i),
choices = ltrs,
options = list(placeholder = "Select letters for this group",
onInitialize = I('function() { this.setValue(""); }')), multiple=T)
})
})
gps <- reactiveValues(x=NULL)
observeEvent(input$submit, {
lapply(1:input$groups, function(i) {
gps$x[[i]] <- input[[paste0("usergroup", i)]]
})
})
test <- session$ns("test")
return(list(dat = reactive({x$data}),
groups = reactive({gps$x})
))
}
UI:
ui <- navbarPage("Fancy Title",id = "tabs",
tabPanel("Panel1",
sidebarPanel(
mod1UI("input1")
),
mainPanel(verbatimTextOutput("lettersy")
)
)
)
Server:
server <- function(input, output, session) {
y <- callModule(mod1, "input1", data=y$dat)
output$lettersy <- renderText({
as.character(c(y$groups()))
})
}
shinyApp(ui, server)

Resources