I am facing a issue to access my shiny module's dynamic input value from slider in the main shiny server function.
In the following code from SelectInput when metric2 is selected, I should be able to see a slider with a specific range and a default. That works perfectly. But the value from the slider is expected to be shown as TextOutput in the main shiny server function, which is failing.
main shiny file:
library(shiny)
source("modules/showLowHighSliders.R")
ui <- fluidPage(
fluidRow(
column(
3,
selectizeInput(
inputId = "metricID",
selected = NULL,
multiple = TRUE,
label = "Select a metric",
choices = list(
Type1 = c("metric1", "metric2", "metric3"),
Type2 = c("metric4","metric5")
),
options = list('plugins' = list('remove_button'))
)
),
column(2,
uiOutput(outputId = "lowerThresholdText"),
showLowHighSlidersUI("sliders-metric2")
)
)
)
server <- function(input, output){
ret <- callModule(module = showLowHighSliders,
id = "sliders-metric2",
metrics_selected=reactive(input$metricID))
output$lowerThresholdText <- renderText({
if(!is.null(input$metricID )){
if(input$metricID == 'metric2'){
paste("Lower Value: ", ret() )
}
}
})
}
shinyApp(ui, server)
Shiny module: showLowHighSliders.R
showLowHighSlidersUI <- function(id) {
ns <- NS(id)
fluidPage(
fluidRow(
column(12,
tagList(
# uiOutput(ns("lowerThresholdText")),
uiOutput(ns("lowerThresholdSlider"))
)
)
)
)
}
# Function for module server logic
showLowHighSliders <- function(input, output, session, metrics_selected) {
reactive({input$mySlider})
output$lowerThresholdSlider <- renderUI({
#print(metrics_selected())
if(!is.null(metrics_selected() ) ){
if('metric2' %in% metrics_selected() ){
sliderInput(
inputId = "mySlider",
label = "",
min = 0,
max = 200,
value = 20
)
}
}
})
# output$lowerThresholdText <- renderText({
# #print(metrics_selected)
# if(!is.null(metrics_selected() )){
# if('SMA' %in% metrics_selected()){
# paste("Lower SMA: ", input$mySlider )
# }
# }
#
# })
}
I was also unable to access the dynamic input slider value within the module itself shown in the commented part.
Any help is appreciated.
Try this
# Function for module server logic
showLowHighSliders <- function(input, output, session, metrics_selected) {
# reactive({input$mySlider})
ns <- session$ns
output$lowerThresholdSlider <- renderUI({
#print(metrics_selected())
if(!is.null(metrics_selected() ) ){
if('metric2' %in% metrics_selected() ){
sliderInput(
inputId = ns("mySlider"),
label = "",
min = 0,
max = 200,
value = 20
)
}
}
})
output$lowerThresholdText <- renderText({
#print(metrics_selected)
if(!is.null(metrics_selected() )){
#if('SMA' %in% metrics_selected()){
paste("Lower SMA: ", session$input$mySlider )
#}
}
})
}
Related
I currently have a table being generated and I would like the user to be able to create a pdf file when they click the download button.
I am currently getting an error where when I click the download button I get an html file that downloads the entire page of the app. I thought that using pdf(file) would work but it ignores the function.
Here is currently what I have.
library(shiny)
library(xlsx)
library(shinyWidgets)
population <- read.xlsx("population.xlsx", 1)
fieldsMandatory <- c("selectedCountry")
labelMandatory <- function(label) {
tagList(
label,
span("*", class = "mandatory_star")
)
}
appCSS <-
".mandatory_star {color: red;}"
ui <- fluidPage(
navbarPage(title = span("Spatial Tracking of COVID-19 using Mathematical Models", style = "color:#000000; font-weight:bold; font-size:15pt"),
tabPanel(title = "Model",
sidebarLayout(
sidebarPanel(
shinyjs::useShinyjs(),
shinyjs::inlineCSS(appCSS),
div(
id = "dashboard",
pickerInput(
inputId = "selectedCountry",
labelMandatory ("Country"),
choices = population$Country,
multiple = FALSE,
options = pickerOptions(
actionsBox = TRUE,
title = "Please select a country")
),
sliderInput(inputId = "agg",
label = "Aggregation Factor",
min = 0, max = 50, step = 5, value = 10),
actionButton("go","Run Simulation"),
)
),
mainPanel(
tabsetPanel(
tabPanel("Input Summary", verbatimTextOutput("summary"),
tableOutput("table"),
downloadButton(outputId = "downloadSummary", label = "Save Summary"))
)
)
)
)
)
)
server <- function(input, output, session){
observeEvent(input$resetAll, {
shinyjs::reset("dashboard")
})
values <- reactiveValues()
values$df <- data.frame(Variable = character(), Value = character())
observeEvent(input$go, {
row1 <- data.frame(Variable = "Country", Value = input$selectedCountry)
row2 <- data.frame(Variable = "Aggregation Factor", Value = input$agg)
values$df <- rbind(row1, row2)
})
output$table <- renderTable(values$df)
observe({
# check if all mandatory fields have a value
mandatoryFilled <-
vapply(fieldsMandatory,
function(x) {
!is.null(input[[x]]) && input[[x]] != ""
},
logical(1))
mandatoryFilled <- all(mandatoryFilled)
# enable/disable the submit button
shinyjs::toggleState(id = "go", condition = mandatoryFilled)
})
output$downloadSummary <- downloadHandler(
filename = function(file) {
paste('my-report.pdf', )
},
content = function(file) {
pdf(file)
}
)
}
shinyApp(ui,server)
Here's a minimal example:
library(shiny)
ui <- fluidPage(
downloadButton("savepdf", "Save pdf")
)
server <- function(input, output, session) {
output$savepdf <- downloadHandler(
filename = "test.pdf",
content = function(file) {
pdf(file)
plot(iris$Sepal.Length, iris$Sepal.Width)
dev.off()
}
)
}
shinyApp(ui, server)
Also see here.
Here is a minimal example with the package latexpdf. It will create the pdf table in the folder of the app.
library(shiny)
library(latexpdf)
dat <- head(iris, 5)
ui <- fluidPage(
br(),
actionButton("dwnld", "Create pdf"),
tableOutput("mytable")
)
server <- function(input, output, session){
output[["mytable"]] <- renderTable({
dat
})
observeEvent(input[["dwnld"]], {
as.pdf(dat)
})
}
shinyApp(ui, 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)
I have a weird issue with conditionalPanel in shiny dashboard.
I modularized my chart UI components as I need to call it multiple times.
The conditional Panel seems to work fine if I call it only once. However, if I attempted to call more than once, it stopped working.
Below is the reproducible code:
library(shiny)
library(shinyWidgets)
library(shinydashboard)
library(shinydashboardPlus)
library(highcharter)
library(lubridate)
chartUI <- function(id) {
ns <- NS(id)
tagList(
verbatimTextOutput(ns("group")),
selectInput(ns("freq"),"Select frequency:",
choices = list("Yearly" = "Y","Half yearly" = "H","Quarterly" = "Q",
"Monthly"="M"), selected = "Yearly", multiple = FALSE),
dateInput(ns("dates"), "Select start date:",format = "yyyy-mm-dd", startview = "month", value = dmy("1/1/2014")),
selectInput(ns("link"),"Select link ratio:",choices = list("All" = "all", "Standard" = "std"),selected = "all"),
conditionalPanel("input.link == 'std'", ns=ns, sliderInput(ns("std.month"),"No of months:",min=1,max=119,value=60))
)
}
ui <- shinyUI(
ui = dashboardPagePlus(skin = "red",
header = dashboardHeaderPlus(
title = "TITLE",
titleWidth = 700
),
dashboardSidebar(),
body = dashboardBody(
# boxPlus(
# width = NULL,title = "CHART",closable = TRUE,enable_sidebar = TRUE,
# sidebar_width = 15,sidebar_start_open = FALSE,sidebar_content = chartUI("chartui1"),
# highchartOutput("")
# ),
boxPlus(
width = NULL,title = "CHART",closable = TRUE,enable_sidebar = TRUE,
sidebar_width = 15,sidebar_start_open = FALSE,sidebar_content = chartUI("chartui2"),
highchartOutput("")
)
),
title = "DashboardPage"
)
)
server <- shinyServer(function(input, output) {
})
shinyApp(ui, server)
If I only call chartui2, conditional panel works fine. But if I call both chartui1 and chartui2, both of them no longer work.
A minimal example with uiOutput / renderUI would be:
library(shiny)
dyn_ui <- function(id) {
ns <- NS(id)
tagList(selectInput(ns("show"), "show or not", choices = c("hide", "show")),
uiOutput(ns("dyn")))
}
dyn_server <- function(input, output, session) {
output$dyn <- renderUI({
ns <- session$ns
if (input$show == "show") {
sliderInput(
inputId = ns("std_month"),
"No of months:",
min = 1,
max = 119,
value = 60
)
}
})
}
ui <- basicPage(dyn_ui("test"))
server <- function(input, output, session) {
callModule(module = dyn_server, id = "test")
}
runApp(list(ui = ui, server = server))
Edit:
In fact, a minimal example works well with conditionalPanel too (see below). So something else about your app is causing a conflict. Not sure what it is, but I would start adding components one by one and see when these minimal examples start misbehaving.
library(shiny)
dyn_ui <- function(id) {
ns <- NS(id)
tagList(
selectInput(ns("show"), "show or not", choices = c("hide", "show")),
conditionalPanel(
ns = ns,
condition = "input.show == 'show'",
sliderInput(
inputId = ns("std_month"),
"No of months:",
min = 1,
max = 119,
value = 60
)
)
}
ui <- basicPage(
dyn_ui("test"),
dyn_ui("test2")
)
server <- function(input, output, session) {
}
runApp(list(ui = ui, server = server))
I have a Shiny App and I am trying to have two sliders appear only if the checkBox is selected. Below is the code I am trying to get to work and am not seeing the UI.
library(shiny)
ui <- fluidPage(
checkboxInput("box_checked", "box_checked", value = FALSE),
uiOutput("test")
)
# Define server logic
server <- function(input, output) {
output$test = renderUI({
if (input$box_checked = 0){
return(NULL)
}
if(input$box_checked = 1){
sliderInput("sliderOne", "Choose your value", min=0, max=100, value=50)
sliderInput("sliderTwo", "Choose your other value", min=0, max=50, value=25)
}
})
}
# Run the application
shinyApp(ui = ui, server = server)
Try this way:
library(shiny)
ui <- fluidPage(checkboxInput("box_checked", "box_checked", value = FALSE),
uiOutput("test"))
# Define server logic
server <- function(input, output) {
output$test = renderUI({
if (input$box_checked == 0) {
return(NULL)
}
if (input$box_checked == 1) {
list(
sliderInput(
"sliderOne",
"Choose your value",
min = 0,
max = 100,
value = 50
),
sliderInput(
"sliderTwo",
"Choose your other value",
min = 0,
max = 50,
value = 25
)
)
}
})
}
# Run the application
shinyApp(ui = ui, server = server)
I fixed if statement, as you used input$box_checked = 1 instead of input$box_checked == 1.
You should use list() to produce multiple UI elements inside renderUI.
I've been searching around and cannot find an answer to my question. I've constructed a simple app to demonstrate my problem. Basically, the problem is that I am trying to use a renderUI inside my module server to conditionally create a uiOutput in the module UI. I've included a few print statements that lead me to believe that the renderUI is evaluated without input being updated. It is killing me that I can't figure this out, and I'd appreciate any help possible!
Example code:
library(shiny) # shiny_1.0.0
library(DT) # DT_0.2
testModuleUI <- function(id) {
ns = NS(id)
tagList(
br(),
sidebarPanel(width = 12, id = "inputBar",
fluidRow(
column(width = 2, checkboxInput(ns("buttonA"), label = "Button A", value = F)),
column(width = 2, uiOutput(ns("getButtonB")))
),
dataTableOutput(outputId = ns("tableOutput"))
)
)
}
testModule <- function(input, output, session, showB = F ){
ns = session$ns
output$getButtonB <- renderUI({
if( showB ){
print("call checkboxInput")
checkboxInput(ns("buttonB"), label = "Button B", value = F)
}else{
NULL
}
})
getTable <- reactive({
print("inside getTable")
out = c()
if( input$buttonA ) {
out = paste0(out, "A")
}
if( input$buttonB ){
out = paste0(out, "B")
}
data.frame(var = out)
})
output$tableOutput <- renderDataTable({
print("call getTable")
datatable( getTable() )
})
}
server <- function(input, output, session) {
callModule( module = testModule, id = "test1", showB = T )
session$onSessionEnded( stopApp )
}
ui <- pageWithSidebar(
headerPanel( title = "Test app" ),
sidebarPanel(
width = 3,
selectInput(inputId = "whatever", label = "This button doesn't matter", choices = c("A", "B"))
),
mainPanel(
tabsetPanel(
tabPanel("Tab 1", testModuleUI("test1"))
)
)
)
shinyApp( ui = ui, server = server, options = list(launch.browser = T)
)
Thank you!!