I am trying to lift state of the number of bins one level up from a module. This is a common technique in react, and I suspect shiny as well, when some data needs to be shared between different components (modules in shiny parlance)
This is the code I currently have
ui.R
library(shiny)
library(shinydashboard)
source("modules/my.R", local=my <- new.env())
ui <- dashboardPage(
dashboardHeader(title="Dashboard"),
dashboardSidebar(
sidebarMenu(
menuItem("App 1", tabName="app1"),
menuItem("App 2", tabName="app2"),
id="selectedMenu"
)
),
dashboardBody(
uiOutput("foo")
)
)
server.R
library(shiny)
source("modules/my.R", local=my <- new.env())
server <- function(input, output) {
reactive = reactive({3})
callModule(my$my, "foo", numBins=reactive)
plot <- my$myUI("foo")
output$foo <- renderUI({
if (input$selectedMenu == "app1") {
return(plot)
} else {
return(br())
}
})
}
and this is the module
library(shiny)
myUI <- function(id) {
ns <- NS(id)
tagList(
fluidRow(
box(
plotOutput(outputId = ns("distPlot")),
width=12
)
),
fluidRow(
box(
sliderInput(inputId = ns("bins"), label = "Number of bins:", min = 1, max = 50, value = 30),
width=12
)
)
)
}
my <- function(input, output, session, numBins) {
output$distPlot <- renderPlot({
numBins()
x <- faithful$waiting
bins <- seq(min(x), max(x), length.out = numBins() + 1)
hist(x, breaks = bins, col = "#75AADB", border = "white",
xlab = "Waiting time to next eruption (in mins)",
main = "Histogram of waiting times")
})
observe({
updateSliderInput(session, "bins", value=numBins())
})
}
I am trying to lift numBins out at the server.R level, and inject it in the module. However, it seems that the plot is not re-rendered. Initialisation seems to work however. I get the right number of bins, but moving the slider does nothing.
Please feel free to comment on other things that look dodgy. I am just a beginner with shiny and R (I do however have experience with react)
Edit
I have a simpler version with just two sliders, trying to make one change when the other is moved, by having numBins shared between the two from below.
library(shiny)
library(shinydashboard)
source("modules/my.R", local=my <- new.env())
ui <- dashboardPage(
dashboardHeader(title="Dashboard"),
dashboardSidebar(),
dashboardBody(
uiOutput("central")
)
)
library(shiny)
source("modules/my.R", local=my <- new.env())
server <- function(input, output) {
numBins = reactiveVal(value=3)
callModule(my$my, "slider1", id="slider1", numBins=numBins)
callModule(my$my, "slider2", id="slider2", numBins=numBins)
output$central <- renderUI({
tagList(
my$myUI("slider1"),
my$myUI("slider2")
)})
}
library(shiny)
myUI <- function(id) {
ns <- NS(id)
fluidRow(
box(
sliderInput(inputId = ns("bins"), label = "Number of bins:", min = 1, max = 50, value = 30),
width=12
)
)
}
my <- function(input, output, session, id, numBins) {
ns <- NS(id)
observeEvent(
numBins,
{
cat("1234", file=stderr())
updateSliderInput(session, ns("bins"), value=numBins())
})
}
Still not working and kind of ugly to have to provide the id twice for the server function.
I'm trying to answer your edited example with two synced sliders. My solution is to let the module return the value of the sliderInput, and also receive an input coupledValue which is used in in observeEvent to update the sliderInput value.
my.R
Somewhat counterintuitively (at least to me when I first learned about it), you do not need to wrap the id "bins" into an ns() inside the updateSliderInput().
library(shiny)
myUI <- function(id) {
ns <- NS(id)
fluidRow(
box(
sliderInput(inputId = ns("bins"), label = "Number of bins:", min = 1, max = 50, value = 30),
width=12
)
)
}
my <- function(input, output, session, id, coupledValue) {
observeEvent(coupledValue(), {
updateSliderInput(session, "bins", value=coupledValue())
})
return(reactive(input$bins))
}
server.R
The numBins() reactive becomes unnecessary, as well as the additional environment you provided within source().
library(shiny)
source("modules/my.R")
server <- function(input, output) {
valSlider1 <- callModule(my, "slider1", id="slider1", coupledValue = valSlider2)
valSlider2 <- callModule(my, "slider2", id="slider2", coupledValue = valSlider1)
}
ui.R
library(shiny)
library(shinydashboard)
source("modules/my.R")
ui <- dashboardPage(
dashboardHeader(title="Dashboard"),
dashboardSidebar(),
dashboardBody(
myUI("slider1"),
myUI("slider2")
)
)
If you want to sync to multiple inputs, you can use return(list(input1 = ..., input2 = ...)) as your return value from the module. When you pass that whole named list into another module, e.g. with the name coupledValues, you will have to reference it as coupledValues$input1() and coupledValues$input2() (note the () after the $).
Discalimer: This answer is based on In sync sliderInput and textInput
I am not sure if this is the best use case for shiny modules. Anyways, here's a way without using modules. Let me know if using modules is a must and I'll try and update my answer.
library(shiny)
ui <- fluidPage(
lapply(1:2, function(x) {
sliderInput(paste0("slider", x), paste0("Slider ", x), min = 1, max = 50, value = 30)
}),
verbatimTextOutput("test")
)
server <- function(input, output, session) {
observeEvent(input$slider1, {
if(input$slider1 != input$slider2) {
updateSliderInput(session, "slider2", value = input$slider1)
}
})
observeEvent(input$slider2, {
if(input$slider1 != input$slider2) {
updateSliderInput(session, "slider1", value = input$slider2)
}
})
output$test <- renderPrint({
c("Slider 1" = input$slider1, "Slider 2" = input$slider2)
})
}
shinyApp(ui, server)
Using return works nice for smaller applications, though using a strategy of reactiveValues pays of in larger apps.
I found the strategy in a blog post by rTask Communication between modules and its whims
The idea is to use r as a reactiveValues and pass it to each callModule.
Inside the module, you create a new reactiveValues based on r, e.g. r$my <- reactiveValues()
Then you don't need to return your module output and you don't need to pass any reactive variable except for r
Here I edited your code according to this strategy (and a few minor things, posted already):
ui.R
library(shiny)
library(shinydashboard)
source("modules/my.R")
ui <- dashboardPage(
dashboardHeader(title="Dashboard"),
dashboardSidebar(),
dashboardBody(
myUI("slider1"),
myUI("slider2")
)
)
server.R
library(shiny)
source("modules/my.R")
server <- function(input, output) {
r <- reactiveValues()
numBins = reactiveVal(value=3)
callModule(my, "slider1", id="slider1", r = r)
callModule(my, "slider2", id="slider2", r = r)
}
my.R
library(shiny)
myUI <- function(id) {
ns <- NS(id)
fluidRow(
box(
sliderInput(inputId = ns("bins"), label = "Number of bins:", min = 1, max = 50, value = 30),
width=12
)
)
}
my <- function(input, output, session, id, r) {
r$my <- reactiveValues()
observe({
r$my <- input$bins
})
observeEvent(
r$my,
{
cat("1234", file=stderr())
updateSliderInput(session, "bins", value=r$my)
})
}
Slightly too late to compete for the bounty. But as I have done the thinking, here is my contribution. This differs from all the existing answers in that it neither uses coupled sliders, nor observers.
First let me ensure I understand your intent: You want to pass the number of bins from the slider in the sub-module, back to the parent module, before passing it from the parent module into the output calculation of the (same) sub-module. (If I have misunderstood your intend, see note below for an alternative).
This would make more sense if you were passing values between two different sub-modules. Modules in Shiny are intended to pass their own values within themselves, so as to avoid cluttering the parent module.
If this is your intention, I recommend the following:
UI (essentially unchanged):
library(shiny)
library(shinydashboard)
source("modules/my.R")
ui <- dashboardPage(
dashboardHeader(title="Dashboard"),
dashboardSidebar(
sidebarMenu(
menuItem("App 1", tabName="app1"),
menuItem("App 2", tabName="app2"),
id="selectedMenu"
)
),
dashboardBody(
uiOutput("foo")
)
)
Server:
library(shiny)
source("modules/my.R")
server <- function(input, output) {
resource_numBins = reactive({ # reactive value is defined
if(exists('my_realised')
& !is.null(my_realised$num_bin())){ # conditions to prevent errors/warnings
return(my_realised$num_bins())
}else{
return(3) # required initial value
}
})
# reactive value passed to module
my_realised <- callModule(my, "foo", numBins = resource_numBins)
# my_realised stores values returned by module
output$foo <- renderUI({
if (input$selectedMenu == "app1") {
return(myUI("foo", initial_num_bins = resource_numBins()))
} else {
return(br())
}
})
}
Module (some white space removed):
library(shiny)
myUI <- function(id, initial_num_bins) {
ns <- NS(id)
tagList(
fluidRow( box(
plotOutput(outputId = ns("distPlot")), width=12
) ),
fluidRow( box(
sliderInput(inputId = ns("bins"), label = "Number of bins:",
min = 1, max = 50, value = initial_num_bins),
width=12
) )
)
}
my <- function(input, output, session, numBins) { # module receives value from parent
output$distPlot <- renderPlot({
x <- faithful$waiting
bins <- seq(min(x), max(x), length.out = numBins() + 1)
hist(x, breaks = bins, col = "#75AADB", border = "white",
xlab = "Waiting time to next eruption (in mins)",
main = "Histogram of waiting times")
})
return(list(num_bins = reactive({input$bins}))) # module returns slider value to parent
}
Note that the complexity of resource_numBins is due to it also being used as the initial value, and needing to persist when menu item "App 2" is selected. Without these additional requirements this reactive would simplify to:
resource_numBins = reactive({ # reactive value is defined
return(my_realised$num_bins())
})
As sub-modules will pass values between themselves without first needing to pass a value back to the parent, the other problem you might be seeking to solve is how to use a sub-module to update the value in a parent module. For this I suggest my existing answer here. Either approach will let you use the value from the sub-module in the parent module.
Related
I have a Shiny app with two modules and a user defined function:
The first module creates two numeric inputs with value set to 1 and 2.
The user defined function should take the values of the first module and add 1.
The second module should take the result of the function, add 1 again and render results.
The app throws error Warning: Error in user_function: could not find function "user_function" and I can't figure out why. Any help and explanation would be much appreciated !
Below is the minimum example code.
first_module.R
#Define ui
first_module_ui <- function(id) {
ns <- NS(id)
tagList(
numericInput(
inputId = ns("first_input_1"),
label = "Input 1:",
value = 1
),
numericInput(
inputId = ns("first_input_2"),
label = "Input 2:",
value = 2
)
)
}
#Define server logic
first_module_server <- function(input, output, session) {
return(input)
}
user_function.R
#User defined function
user_function <- function(first_module_res) {
function_result_1 <- reactive({first_module_res$first_input_1 + 1})
function_result_2 <- reactive({first_module_res$first_input_2 + 1})
return(
list(
function_result_1 = function_result_1,
function_result_2 = function_result_2
)
)
}
second_module.R
#Define ui
second_module_ui <- function(id) {
ns <- NS(id)
tagList(uiOutput(outputId = ns("second_input_1")),
uiOutput(outputId = ns("second_input_2")))
}
#Define server logic
second_module_server <- function(input, output, session, function_result) {
ns <- session$ns
function_result_1 <- reactive({function_result$result_1 + 1})
output$second_input <- renderUI({
disabled(textInput(
inputId = ns("second_input_1"),
label = "Second input 1:",
value = function_result_1()
))
})
function_result_2 <- reactive({function_result$result_2 + 1})
output$second_input_2 <- renderUI({
disabled(textInput(
inputId = ns("second_input_2"),
label = "Second input 2:",
value = function_result_2()
))
})
return(
list(reactive({second_input_1()}),
reactive({second_input_2()}))
)
}
app.R
library(shiny)
library(shinyjs)
# Define UI
ui <- fluidPage(
useShinyjs(),
# Application title
titlePanel("Demo"),
# Sidebar
sidebarLayout(
sidebarPanel(
first_module_ui("first")
),
mainPanel(
second_module_ui("second")
)
)
)
# Define server logic
server <- function(input, output, session) {
first_module_res <- callModule(first_module_server, "first")
observe(
function_result <- user_function(first_module_res),
second_module_res <- callModule(second_module_server, "second", function_result)
)
}
# Run the application
shinyApp(ui = ui, server = server)
You have 2 errors in your code:
in app.R, you don't need the observe. If you use observe, you should also enclose the expression into curly braces. However, you also have a comma in the observe which leads to the error
in the second module, you have to use function_result$function_result_1() instead of function_result$result_1()
Also, I named the output IDs of the UI elements differently than the input IDs, I think otherwise it is not good style.
second_module.R
#Define ui
second_module_ui <- function(id) {
ns <- NS(id)
tagList(uiOutput(outputId = ns("UI_second_input_1")),
uiOutput(outputId = ns("UI_second_input_2")))
}
#Define server logic
second_module_server <- function(input, output, session, function_result) {
ns <- session$ns
function_result_1 <- reactive({
function_result$function_result_1() + 1})
output$UI_second_input_1 <- renderUI({
disabled(textInput(
inputId = ns("second_input_1"),
label = "Second input 1:",
value = function_result_1()
))
})
function_result_2 <- reactive({function_result$function_result_2() + 1})
output$UI_second_input_2 <- renderUI({
disabled(textInput(
inputId = ns("second_input_2"),
label = "Second input 2:",
value = function_result_2()
))
})
return(
list(reactive({second_input_1()}),
reactive({second_input_2()}))
)
}
app.R
library(shiny)
library(shinyjs)
# Define UI
ui <- fluidPage(
useShinyjs(),
# Application title
titlePanel("Demo"),
# Sidebar
sidebarLayout(
sidebarPanel(
first_module_ui("first")
),
mainPanel(
second_module_ui("second")
)
)
)
# Define server logic
server <- function(input, output, session) {
first_module_res <- callModule(first_module_server, "first")
function_result <- user_function(first_module_res)
second_module_res <- callModule(second_module_server, "second", function_result)
}
# Run the application
shinyApp(ui = ui, server = server)
I have created a dynamic UI with the number of rows of a 'table' defined by a slider. I would like to use the numericInputs from the UI to perform further calculations. In the example below I have tried to calculate a rate from the two numeric inputs, which seems to work when new values are entered but immediately defaults back to the original starting values.
I tried using a button and changing the observe to an observeEvent to calculate the rates which worked to generate the result, but did not stop the numericInputs defaulting back to the starting values.
I have also tried to create the textboxes as a reactive and then call it to renderUI which gives the same 'broken' functionality.
output$groupings <- renderUI({ textboxes() })
textboxes <- reactive ({
I think I need to create vector or datatable to store the inputs so that I can call them later, however I've been unsuccessful so far. My working example is below:
library(shiny)
mod1UI <- function(id) {
ns <- NS(id)
tagList(
sliderInput(inputId = ns("groups"), label = "Number of Rows", min = 1, max = 6, value = 4, step = 1, width = NULL),
hr(),
fluidRow(
column(2,
strong("Speed")),
column(2,
strong("Amount")),
column(2,
strong("Run Rates"))
),
hr(),
uiOutput(ns("textboxes")),
)
}
mod1 <- function(input, output, session, data) {
ns <- session$ns
m <- reactiveValues(x=NULL)
output$textboxes <- renderUI ({
req(input$groups)
lapply(1:input$groups, function(i) {
fluidRow(
column(2,
numericInput(inputId = paste0(session$ns("speed"),i), value = 700, label = NULL, width = 80)
),
column(2,
numericInput(inputId = paste0(session$ns("amount"),i), value = 14, label = NULL, width = 80)
),
column(2,
(m$x[[i]])
)
)
})
})
observe({
lapply(1:input$groups, function(i){
m$x[[i]] <- input[[paste0("speed", i)]] * input[[paste0("amount", i)]] * 60
})
})
}
ui <- fluidPage(
fluidRow(
column(12,
mod1UI("input1"))
)
)
server <- function(input, output, session) {
y <- callModule(mod1, "input1")
}
shinyApp(ui, server)
Your problem is that you render all elements to one output, output$textboxes. Changing the input value of one of your numeric inputs leads to the calculation of a new rate, so the reactive Value m gets updated and the output$textboxes is rerendered.
Below I present you a solution where the different columns are rendered separately; you would have to play with HTML/CSS to display the values nicely. However, if you change the numbers of rows with the slider, all inputs are reset. Therefore I also added a solution where every row is a module that can be added.
library(shiny)
mod1UI <- function(id) {
ns <- NS(id)
tagList(
sliderInput(inputId = ns("groups"), label = "Number of Rows", min = 1, max = 6, value = 4, step = 1, width = NULL),
hr(),
fluidRow(
column(2,
strong("Speed")),
column(2,
strong("Amount")),
column(2,
strong("Run Rates"))
),
hr(),
fluidRow(
column(2,
uiOutput(ns("UI_speed"))),
column(2,
uiOutput(ns("UI_amount"))),
column(2,
uiOutput(ns("rates")))
)
)
}
mod1 <- function(input, output, session, data) {
ns <- session$ns
m <- reactiveValues(x=NULL)
output$UI_speed <- renderUI({
req(input$groups)
lapply(1:input$groups, function(i) {
numericInput(inputId = paste0(session$ns("speed"),i), value = 700, label = NULL, width = 80)
})
})
output$UI_amount <- renderUI({
req(input$groups)
lapply(1:input$groups, function(i) {
numericInput(inputId = paste0(session$ns("amount"),i), value = 14, label = NULL, width = 80)
})
})
output$rates <- renderUI({
req(input$groups)
text <- lapply(1:input$groups, function(i) {
m$x[[i]]
})
HTML(paste0(text, collapse = "<br>"))
})
observe({
lapply(1:input$groups, function(i){
m$x[[i]] <- input[[paste0("speed", i)]] * input[[paste0("amount", i)]] * 60
})
})
}
ui <- fluidPage(
fluidRow(
column(12,
mod1UI("input1"))
)
)
server <- function(input, output, session) {
y <- callModule(mod1, "input1")
}
shinyApp(ui, server)
Every row is a module
You get more flexibility if you have the slider in the main app and then add/remove a module. The module UI now consists of a set of inputs for Speed and Amount and an Output for the Rate. You can use insertUI and removeUI to dynamically control the amount of modules and with this the amount of displayed UI elements.
library(shiny)
mod1UI <- function(id) {
ns <- NS(id)
fluidRow(
id = id,
column(2,
uiOutput(ns("UI_speed"))),
column(2,
uiOutput(ns("UI_amount"))),
column(2,
textOutput(ns("rates")))
)
}
mod1 <- function(input, output, session, data) {
ns <- session$ns
output$UI_speed <- renderUI({
numericInput(inputId = ns("speed"), value = 700, label = NULL, width = 80)
})
output$UI_amount <- renderUI({
numericInput(inputId = ns("amount"), value = 14, label = NULL, width = 80)
})
output$rates <- renderText({
get_rate()
})
get_rate <- reactive({
input$speed * input$amount * 60
})
}
ui <- fluidPage(
fluidRow(
column(12,
sliderInput(inputId = "groups", label = "Number of Rows", min = 1, max = 6, value = 4, step = 1, width = NULL),
hr(),
fluidRow(
column(2,
strong("Speed")),
column(2,
strong("Amount")),
column(2,
strong("Run Rates"))
),
hr(),
tags$div(id = "insert_ui_here")
)
)
)
number_modules <- 4
current_id <- 1
server <- function(input, output, session) {
# generate the modules shown on startup
for (i in seq_len(number_modules)) {
# add the UI
insertUI(selector = '#insert_ui_here',
ui = mod1UI(paste0("module_", current_id)))
# add the logic
callModule(mod1, paste0("module_", current_id))
# update the id
current_id <<- current_id + 1
}
observeEvent(input$groups, {
# add modules
if (input$groups > number_modules) {
for (i in seq_len(input$groups - number_modules)) {
# add the UI
insertUI(selector = '#insert_ui_here',
ui = mod1UI(paste0("module_", current_id)))
# add the logic
callModule(mod1, paste0("module_", current_id))
# update the id
current_id <<- current_id + 1
}
} else {
# remove modules
for (i in seq_len(number_modules - input$groups)) {
# remove the UI
removeUI(selector = paste0("#module_", current_id - 1))
current_id <<- current_id - 1
}
}
# update the number of modules
number_modules <<- input$groups
}, ignoreInit = TRUE)
}
shinyApp(ui, server)
As an extension of this example:
https://shiny.rstudio.com/gallery/creating-a-ui-from-a-loop.html
Say you would like the for loop to be of a length determined by a numeric input. So for example, extending the linked example (using just the second part of it):
ui <- fluidPage(
title = 'Creating a UI from a dynamic loop length',
sidebarLayout(
sidebarPanel(
# Determine Length of Loop
numericInput(inputId = "NumLoop", "Number of Loops", value = 5, min = 1, max = 5, step = 1)
),
mainPanel(
# UI output
lapply(1:input.NumLoop, function(i) {
uiOutput(paste0('b', i))
})
)
)
)
server <- function(input, output, session) {
reactive({
lapply(1:input$NumLoop, function(i) {
output[[paste0('b', i)]] <- renderUI({
strong(paste0('Hi, this is output B#', i))
})
})
})
}
shinyApp(ui = ui, server = server)
As far as I can tell there are two problems with the code:
In the UI, I don't know how to legitimately use the input from NumLoop in the for loop of the UI output. I have experimented with the conditionalPanel function with no luck.
In the server, once I put the loop behind a reactive function to make use of input$NumLoop I no longer have access to those renderUI outputs in the UI.
Any ideas of how to solves these issues would be much appreciated.
This should do the trick, as per #Dean, yes the second renderUI shouldn't be there
library(shiny)
ui <- fluidPage(
title = 'Creating a UI from a dynamic loop length',
sidebarLayout(
sidebarPanel(
# Determine Length of Loop
numericInput(inputId = "NumLoop", "Number of Loops", value = 5, min = 1, max = 10, step = 1)
),
mainPanel(
# UI output
uiOutput('moreControls')
)
)
)
server <- function(input, output, session) {
output$moreControls <- renderUI({
lapply(1:input$NumLoop, function(i) {
strong(paste0('Hi, this is output B#', i),br())
})
})
}
shinyApp(ui = ui, server = server)
I want to create a vector by using observe() in R shiny. In the code blow, how can I create a vactor where all the input$n are concatenated. At the present time, I can only display a single value but could not concatenate and display all the inputs from the sliderInput.
ui.R
library(shiny)
fluidPage(
titlePanel("Observer demo"),
fluidRow(
column(4, wellPanel(
sliderInput("n", "N:",
min = 10, max = 1000, value = 200, step = 10)
)),
column(8,
tableOutput("text")
)
)
)
server.R
library(shiny)
function(input, output, session) {
observed=reactiveValues(
input=NULL
)
observe({
observed$input=input$n
# observed$input=c(observed$input,input$n) # tried this but not working
})
output$text <- renderTable({
observed$input
})
}
If you add print(observed$input) in your observer, you will see that when you use observed$input=c(observed$input,input$n) you run into an infinite loop as the observe is reactive to observe$input and will run again as soon as you modify it.
To prevent this, you can use isolate:
observed$input=c(isolate(observed$input),input$n)
As in #Pork Chop 's answer, you can also use observeEvent to only observe input$n.
Try this, you can use cbind or rbind depending on your needs
rm(list = ls())
library(shiny)
ui <- fluidPage(
titlePanel("Observer demo"),
fluidRow(
column(4, wellPanel(
sliderInput("n", "N:",
min = 10, max = 1000, value = 200, step = 10)
)),
column(8,
tableOutput("text")
)
)
)
server <- function(input, output, session) {
observed=reactiveValues(
input=NULL
)
observeEvent(input$n,{
observed$input <- cbind(observed$input,input$n)
})
output$text <- renderTable({
print(observed$input)
observed$input
})
}
shinyApp(ui <- ui, server <- server)
I have created a sample app below to illustrate the issue I am having. I have an application in Shiny that is using many layers of modules. I am very familiar with using modules and returning reactive values from the modules themselves. However when I need to use lapply to create multiple calls of modules (in this case slider_menu_item_shiny function to create multiple sliders), each which return the reactive value that is set by the user in sliders, I am not sure how to dynamically capture all of the output reactive variables into one reactive vector.
Right now I have 2 sliders hard coded in and this simple app works. However I want to be able to type in an arbitrary value in the first input, have the app create that amount of slider modules using the lapply statement (for the callModule(slider_menu_item_shiny) call too) and then have slider_value_vector contain a vector of that length with all of the slider values.
I feel like I am missing a fundamental trick to making this work. I would really appreciate the learning experience and all of the help.
ui.R code
library(shiny)
library(shinydashboard)
library(DT)
#### MODULE CODE ####
source("modules.R")
# define header
header <- dashboardHeader(
title = "Test"
)
# define body
body <- dashboardBody(
tabItems(
body_set_shinyUI(id = "body_test_mod", tab_name = "body_test_mod")
)
)
# define sidebar
sidebar <- dashboardSidebar(
sidebarMenu(id = "dashboard_menu",
menuItem("Test Body", tabName = "body_test_mod")
)
)
dashboardPage(skin = "blue",
header,
sidebar,
body
)
server.R code
library(shiny)
library(shinydashboard)
library(DT)
#### MODULE CODE ####
source("modules.R")
#### SERVER CODE ####
function(input, output, session) {
callModule(body_set_shiny, id = "body_test_mod")
}
modules.R code
### body_set_shiny
body_set_shinyUI <- function(id, tab_name) {
ns <- NS(id)
tabItem(tabName = tab_name,
fluidRow(
column(12,
inner_body_test_menu_shinyUI(ns("inner_body_test_mod"))
)
)
)
}
body_set_shiny <- function(input, output, session) {
callModule(inner_body_test_menu_shiny, id = "inner_body_test_mod")
}
### inner_body_test_menu_shiny
inner_body_test_menu_shinyUI <- function(id) {
ns <- NS(id)
fluidRow(
column(12,
box(title = "Test Inner Menu",
width = 12,
fluidRow(
column(12,
wellPanel(
uiOutput(ns("inner_number_menu")),
uiOutput(ns("inner_sliders_menu")),
uiOutput(ns("inner_text_output"))
)
)
)
)
)
)
}
inner_body_test_menu_shiny <- function(input, output, session) {
output$inner_number_menu <- renderUI({
ns <- session$ns
textInput(ns("inner_number_value"), label = "Enter Number of Sliders", value = "2")
})
slider_length <- reactive({
if (is.null(input$inner_number_value))
return()
as.numeric(input$inner_number_value)
})
output$inner_sliders_menu <- renderUI({
if (is.null(slider_length()))
return()
ns <- session$ns
lapply((1:slider_length()), function(m) {
slider_menu_item_shinyUI(ns(paste("slider_menu_item_", m, sep = "")))
})
})
output$inner_text_output <- renderText({
if (is.null(slider_value_vector()))
return()
paste("You have entered", slider_value_vector())
})
slider_value_vector <- reactive({
if (is.null(slider_length()))
return()
c(as.numeric(slider_v1()[[1]]),as.numeric(slider_v2()[[1]]))
})
slider_v1 <- callModule(slider_menu_item_shiny, paste("slider_menu_item_", 1, sep = ""))
slider_v2 <- callModule(slider_menu_item_shiny, paste("slider_menu_item_", 2, sep = ""))
}
slider_menu_item_shinyUI <- function(id) {
ns <- NS(id)
uiOutput(ns('sider_output_menu'))
}
slider_menu_item_shiny <- function(input, output, session, slider_value = 0, slider_name = "No Name Found") {
output$sider_output_menu <- renderUI({
ns <- session$ns
uiOutput(ns("slider_item_menu"))
})
output$slider_item_menu <- renderUI({
ns <- session$ns
sliderInput(ns("slider_item"), label = "Slider Example", min = -1, max = 1, value = 0.5, step = 0.01)
})
return(reactive(list(input$slider_item)))
}