Related
I am new to Shiny. What I want to do in my application is, running & displaying some part of the code only when a condition on another calculation is met.
The conditionalPanel works fine with the conditions on input values but I could not figure out how to do this with the 'output' values, i.e., conditionally on the output values of the functions. Below is my example code:
library(shiny)
msLocation <- "msLoc"
searchMWText <- "searchMW"
bid <- "2333333"
fulltext <- "fullDisplay"
ui <- fluidPage(
titlePanel("Run server codes conditionally"),
sidebarLayout(
sidebarPanel(
helpText("Evaluate input and run different parts of the code depending on the output functions"),
br(),
sliderInput("rand", "select seed", min = 1, max = 50, step = 1, value = 1)
),
mainPanel(
fluidRow(conditionalPanel("output.rand == 1"),
tags$h4("Here comes the default part"),
br(),
textOutput("defaultCalc")),
fluidRow(conditionalPanel("output.randomint != 1",
tags$h4("I can evaluate if the chosen number is even or odd."),
br(),
textOutput("evenodd")
),
fluidRow(conditionalPanel("output.evenodd == 'Number is even'",
tags$h4("Number even calculation "),
textOutput("msLoc"),
br(),
textOutput("searchMW"),
br(),
textOutput("defaultID"),
br()
),
fluidRow(conditionalPanel("output.evenodd == 'Number is odd'",
tags$h4("Here is some id:", textOutput("id")),
textOutput("displayFull")
)
)
)
)
)))
#
server <- function(input, output) {
rand1 <- reactive({
if(is.null(input$rand)){return(NULL)}
rn <- input$rand
return(rn)
})
randomint <- reactive({
seedn <- rand1()
set.seed(seedn)
rint <- sample(1:50, 1)
return(rint)
})
calc1 <- reactive({
intn <- randomint()
modn <- intn %% 2
return(modn)
})
evenOdd <- reactive({
modn <- calc1()
if(modn == 0){valueText = "Number is even"}
if(modn != 0){valueText = "Number is odd"}
return(valueText)
})
idtext <- reactive({
idint <- sample(1:10000, 3)
idint <- as.character(idint)
idint <- paste(idint, collapse = "")
return(idint)
})
output$defaultCalc <- renderText({
as.character(randomint())
})
output$evenodd <- renderText({
evenOdd()
})
output$searchMW <- renderText({
searchMWText
})
output$defaultID <- renderText({
bid
})
output$id <- renderText({
idtext()
})
output$displayFull <- renderText({
fulltext
})
}
shinyApp(ui = ui, server = server)
The problem is, the parts after default always appear, e..g., 'Here is some id' text always appears and this is not what I want. I want to display 'Here is some id' and run the calculation (idtext) only when the number is odd.The number is not coming from the slider input, the slider input is providing the seed only. The number is also calculated and depends on its value, the other parts should be run and displayed. Until the user selects a slider input value, only the 'default part' should be displayed and nothing else.
I searched a lot and could not find a solution that mentions the conditions on output. What is the best way to solve this?
Do:
randomint <- reactive({
seedn <- rand1()
set.seed(seedn)
rint <- sample(1:50, 1)
return(rint)
})
output$randomint <- reactive(randomint())
outputOptions(output, "randomint", suspendWhenHidden = FALSE)
Then you can use "output.randomint !== 1".
I have an app which creates boxes. Each box has a button that triggers a modal. The modal has inputs which the user changes and then a button which triggers an action based on those inputs (basically just uploading to a database). Because each box has a different specification, I wrote a module and then loop thru a list, creating a box for each element. This works fine.
However, the flow in the modal and observeEvent has a flaw: the first run thru I get the desired results, but on the second occasion in the same box (same id module), after pressing the modal button to update, it will not use the new inputs, but rather what happened in the first run. I am guessing it has something to do with the namespace/observeEvent combination as I might be triggering the event with a "stored" namespace? Would I need to somehow "flush" the namespace after every update? Anyway, any help appreciated as it gets confusing fast with all the namespace/modules combinations.
library(shiny)
library(shinyWidgets)
ui <- navbarPage(
'page', collapsible = TRUE,
tabPanel("test",
useSweetAlert(),
sidebarLayout(
sidebarPanel(),
mainPanel(
uiOutput('all_products_ui')
)
)
)) # end navbar
server <- shinyServer(function(input, output) {
list_products <- c(1,2,3,4,5)
# Now, I will create a UI for all the products
output$all_products_ui <- renderUI({
r <- tagList()
progress_move <- 0
for(k in 1:length( list_products )){
r[[k]] <- ExistingProductUI(id = k, product = list_products[[k]] )
}
r
})
# handlers duplicate a call to module depending on the id of ExistingProductUI
handlers <- list()
observe(
handlers <<- lapply(seq.int(length( list_products )),
function(i) {
callModule(ExistingProductUpdate,
id = i,
product = list_products[[i]] )
})
)
handlers
}) # end of server ----
# UI module ------------------------------------------------------
ExistingProductUI <- function(id, product){
ns <- NS(id)
box(title = as.character(p$title),
product["title"],
footer = tagList(
actionBttn(
inputId = ns("change_selected"), label = "change"),
)
)
}
# server module ------------------------------------------------------
ExistingProductUpdate <- function(input, output, session, product){
ns <- session$ns
observeEvent(input$change_selected, {
# when box button is clicked for this product (id)
# FIRST: show a modal
showModal(
modalDialog(
title = "what do you want to change?",
tagList(
radioGroupButtons(inputId = ns("change_selected_choice"), labels = "change x", choices = c(1,2,3,4)),
sliderInput(ns("change_selected_pct"), "change y:", min = -50, max = 100, value = 0, step = 5)
),
easyClose = TRUE,
footer = tagList(
actionButton(ns("change_selected_submit"), "submit!", icon = icon("check")),
modalButton("never mind")
)
)
)
# SECOND: when change_selected_submit is clicked,
observeEvent(input$change_selected_submit, {
# do some calculations with product using what I inputed in modal ---
# then, update a table ----
functionToUploadThings(product, input$change_selected_choice)
# THIRD: Close with a confirmation
sendSweetAlert(
session,
title = "Success!",
type = "success",
btn_labels = "Ok",
closeOnClickOutside = TRUE,
width = NULL
)
})
})
}
Below is a solution that works. The problem was that you nested your observeEvent in the module. I'm not entirely sure why this led to problems, some values weren't processed correctly. However, you don't need to nest the observeEvent, the second one gets also triggered by the actionButton in the modal when it is by its own. Additionally, I included a removeModal before the success notification is shown:
library(shiny)
library(shinyWidgets)
library(shinydashboard)
ui <- navbarPage(
'page', collapsible = TRUE,
tabPanel("test",
useSweetAlert(),
sidebarLayout(
sidebarPanel(),
mainPanel(
uiOutput('all_products_ui')
)
)
)) # end navbar
server <- shinyServer(function(input, output) {
list_products <- c(1,2,3,4,5)
# Now, I will create a UI for all the products
output$all_products_ui <- renderUI({
r <- tagList()
progress_move <- 0
for(k in 1:length( list_products )){
r[[k]] <- ExistingProductUI(id = k, product = list_products[[k]] )
}
r
})
# handlers duplicate a call to module depending on the id of ExistingProductUI
handlers <- list()
observe(
handlers <<- lapply(seq.int(length( list_products )),
function(i) {
callModule(ExistingProductUpdate,
id = i,
product = list_products[[i]] )
})
)
handlers
}) # end of server ----
# UI module ------------------------------------------------------
ExistingProductUI <- function(id, product){
ns <- NS(id)
box(title = as.character(product),
product,
footer = tagList(
actionBttn(
inputId = ns("change_selected"), label = "change"),
)
)
}
# server module ------------------------------------------------------
ExistingProductUpdate <- function(input, output, session, product){
ns <- session$ns
observeEvent(input$change_selected, {
# when box button is clicked for this product (id)
# FIRST: show a modal
showModal(
modalDialog(
title = "what do you want to change?",
tagList(
radioGroupButtons(inputId = ns("change_selected_choice"), label = "change x", choices = c(1,2,3,4)),
sliderInput(ns("change_selected_pct"), "change y:", min = -50, max = 100, value = 0, step = 5)
),
easyClose = TRUE,
footer = tagList(
actionButton(ns("change_selected_submit"), "submit!", icon = icon("check")),
modalButton("never mind")
)
)
)
})
# SECOND: when change_selected_submit is clicked,
observeEvent(input$change_selected_submit, {
# do some calculations with product using what I inputed in modal ---
# then, update a table ----
# functionToUploadThings(product, input$change_selected_choice)
# THIRD: Close with a confirmation
removeModal()
sendSweetAlert(
session,
title = "Success!",
type = "success",
btn_labels = "Ok",
closeOnClickOutside = TRUE,
width = NULL
)
})
}
shinyApp(ui, server)
Please note: I made some modifications to make your MWE work:
include library(shinydashboard)
p$title and product["title"] to product
change labels to label in radioGroupButtons
comment out functionToUploadThings(product, input$change_selected_choice)
Edit
I'm still not super sure what happens when nesting the observeEvents. I made a small toy example and played around with the reactlog. It seems that nesting the observers generates a new observer for button2 every time button1 is clicked. These observers are not removed and lead to unwanted behaviour. In contrast, when using separate observeEvents, the observer for button2 is only created once.
library(shiny)
library(reactlog)
ui <- fluidPage(
actionButton("button1", "click")
)
server <- function(input, output, session) {
observeEvent(input$button1, {
print("from first observer")
print(input$button2)
showModal(
modalDialog(
title = "what do you want to change?",
"some text",
easyClose = TRUE,
footer = tagList(
actionButton("button2", "submit!", icon = icon("check")),
modalButton("never mind")
)
)
)
# nested observer -> leads to remaining observers
observeEvent(input$button2, {
print("from second observer")
print(input$button2)
removeModal()
})
})
# independent observer -> generates only one observer
# observeEvent(input$button2, {
# print("from second observer")
# print(input$button2)
# removeModal()
# })
}
shinyApp(ui, server)
So this is an extension to my previous question.
Dynamic repeating conditionalPanel in R shiny dashboard
Here is the shiny code I am using right now.
library(shiny)
library(shinydashboard)
ui <- fluidPage(
br(),
selectInput("inpt", "Input Number", seq(1,50), selectize = FALSE),
br(),
uiOutput("selectors")
)
server <- function(input, output, session){
output[["selectors"]] <- renderUI({
n <- input[["inpt"]]
selectors <- lapply(1:n, function(i){
selectInput(paste0("id",i), "Select number", seq(1,24), selected = 1)
})
do.call(function(...){
box(..., width = 2, status = "primary")
}, selectors)
})
}
shinyApp(ui, server)
It will generate selection windows depending on 'inpt' number selection.
Now my issue is that I want to access the value of generated selection input.
Example: If I have selected 3, three inputs will be generated with id1, id2, id3.
How to access these ids? If I want to print them, how can I?
for (j in 1:inpt){
print(eval(parse(text = paste0("input$", paste0("id",j)))))
}
But output for this is:
NULL
NULL
NULL
I thought my eval and parse method is wrong so I tried with just inpt
for (j in 1:inpt){
print(eval(parse(text = paste0("input$", paste0("in","pt")))))
}
Output was (3 was selected in selection input)
3
3
3
So my eval, parse method was correct I guess.
So how to access id1, id2, ..., idn in above example?
Please check the following:
library(shiny)
library(shinydashboard)
ui <- fluidPage(
br(),
selectInput("inpt", "Input Number", seq(1,50), selectize = FALSE),
br(),
uiOutput("selectors"),
uiOutput("printMyDynamicInputs"),
uiOutput("printMyFirstDynamicInput")
)
server <- function(input, output, session){
output[["selectors"]] <- renderUI({
n <- input[["inpt"]]
selectors <- lapply(1:n, function(i){
selectInput(paste0("id",i), "Select number", seq(1,24), selected = 1)
})
do.call(function(...){
box(..., width = 2, status = "primary")
}, selectors)
})
myDynamicInputs <- reactive({
lapply(1:input$inpt, function(i){
input[[paste0("id",i)]]
})
})
output$printMyDynamicInput <- renderUI({
paste("You selected:", paste(myDynamicInputs(), collapse = ", "))
})
output$printMyFirstDynamicInputs <- renderUI({
paste("You selected:", input$id1)
})
}
shinyApp(ui, server)
I want to be able to add or remove new words every time I click the "Add words"/"Remove words" buttons. But I want the app to save the previously added/removed words. For example, if I add 'hello' as my first word, and then I add 'bye', I want my vector of words to be ['hello', 'bye']. If I then remove 'hello', my vector should be ['bye']
This is what I have been able to achieve so far
# Define UI ----------
ui <- fluidPage(
# Sidebar panel
sidebarLayout(
sidebarPanel(
selectInput('addrm',
label = h3('Remove or add words'),
choices = list('Remove words' = 1,
'Add words' = 2)),
textInput('words',
label = 'You can introduce multiple words separated by comma',
placeholder = 'Enter words...'),
uiOutput('button')
),
# Main panel
mainPanel(
textOutput('removeWords')
)
)
)
# Define server logic required ------------
server <- function(input, output){
output$button <- renderUI({
if (input$addrm == 1){
actionButton('button', label = 'Remove words')
} else{
actionButton('button', label = 'Add words')
}
})
stopwords <- c()
output$removeWords <- renderText({
input$button
isolate({ # Only runs when the button is clicked
stopwords <- unique(c(stopwords, unlist(strsplit(gsub(' ', '', input$words), ','))))
})
})
}
# Run the application
shinyApp(ui = ui, server = server)
Nevermind, I was able to find what I was looking for in the following post
Just in case anyone wants the answer:
# Define UI ----------
ui <- fluidPage(
# Sidebar panel
sidebarLayout(
sidebarPanel(
selectInput('addrm',
label = h3('Remove or add words'),
choices = list('Remove words' = 1,
'Add words' = 2)),
textInput('words',
label = 'You can introduce multiple words separated by comma',
placeholder = 'Enter words...'),
uiOutput('button')
),
# Main panel
mainPanel(
textOutput('removeWords')
)
)
)
# Define server logic required ------------
server <- function(input, output){
output$button <- renderUI({
if (input$addrm == 1){
actionButton('button', label = 'Remove words')
} else{
actionButton('button', label = 'Add words')
}
})
values <- reactiveValues()
values$stopwords <- c()
output$removeWords <- renderText({
input$button
isolate({ # Only runs when the button is clicked
values$stopwords <- unique(c(values$stopwords, unlist(strsplit(gsub(' ', '', input$words), ','))))
})
})
}
# Run the application
shinyApp(ui = ui, server = server)
I have a sidebarLayout app in which I've set up buttons to add and remove tabPanels in the sidebarPanel. However, I can't figure out how to customize those tabPanels. My code is below:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(width = 3, fixed=T,
h3("L2 Machine"),
actionButton('moreL2', tags$b('Add L2')),
actionButton('lessL2', tags$b('Remove L2')),
uiOutput('panelset'),
tabPanel("L2panel",
numericInput(inputId='L2amount', 'Select L2 Amount', value=0),
selectInput(inputId='L2type', 'Select L2 Type', c('Percent', 'Absolute')),
uiOutput('L2daterange')
)
),
mainPanel(
verbatimTextOutput('L2a'),
verbatimTextOutput('L2t')
)
)
)
server <- function(input, output) {
output$L2a <- renderPrint(input$L2amount)
output$L2t <- renderPrint(input$L2type)
output$panelset <- renderUI({
n <- seq(max(input$moreL2 - input$lessL2 + 1, 1))
tabList <- lapply(paste("Pan", n), tabPanel)
do.call(tabsetPanel, tabList)
})
output$L2daterange <- renderUI({
dateRangeInput(inputId='L2daterange',
label='Select Adjustment Period',
start='01-01-2010', end='01-12-2015'
)
})
}
shinyApp(ui, server)
Currently, I have numericInput(), selectInput(), and uiOutput() inside tabPanel(). Instead I want each tabPanel created by clicking the button "Add L2" to have it's own set of numericInput, selectInput and uiOutput.
You create indeed different tabPanels but they are empty - both numericInput and selectInput are not inside of dynamic tabPanels. The solution is based on https://gist.github.com/wch/5436415/ and you can find there an extensive explanation why do you need a function local to render outputs with a for loop.
As said above, you created correctly dynamic tabPanels but they are empty. Within lapply you should specify unique widgets as arguments to tabPanel.
output$panelset <- renderUI({
n <- seq(max(input$moreL2 - input$lessL2 + 1, 1))
tabList <- lapply(paste("Pan", n), tabPanel)
do.call(tabsetPanel, tabList)
})
Here I coded an example of how you can do it the correct way. Each time you create a unique tabPanel with a unique set of widgets.
tabList <- lapply(n, function(i) {
tabPanel(
title = paste0('Pan', i),
numericInput(inputId = paste0('L2amount', i), 'Select L2 Amount', value = 0),
selectInput(inputId = paste0('L2type', i), 'Select L2 Type', c('Percent', 'Absolute')),
dateRangeInput(inputId = paste0('L2daterange',i),
label = 'Select Adjustment Period',
start = '01-01-2010', end = '01-12-2015'))
})
do.call(tabsetPanel, tabList)
})
Then for each tabPanel with unique set of widgets you have to create unique set of outputs and then you can render values of your widgets.
Full solution:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(width = 3, fixed=T,
h3("L2 Machine"),
actionButton('moreL2', tags$b('Add L2')),
actionButton('lessL2', tags$b('Remove L2')),
uiOutput('panelset')
),
mainPanel(
uiOutput("dynamic")
)
)
)
TMAX <- 10 # specify maximal number of dynamic panels
server <- function(input, output) {
output$panelset <- renderUI({
n <- seq(max(input$moreL2 - input$lessL2 + 1, 1))
# You have to create each time a new set of unique widgets
tabList <- lapply(n, function(i) {
tabPanel(
title = paste0('Pan', i),
numericInput(inputId = paste0('L2amount', i), 'Select L2 Amount', value = 0),
selectInput(inputId = paste0('L2type', i), 'Select L2 Type', c('Percent', 'Absolute')),
dateRangeInput(inputId = paste0('L2daterange',i),
label = 'Select Adjustment Period',
start = '01-01-2010', end = '01-12-2015'))
})
do.call(tabsetPanel, tabList)
})
output$dynamic <- renderUI({
n <- seq(max(input$moreL2 - input$lessL2 + 1, 1))
# You want to render n-times different outputs and each time you have
# k different outputs -- > need a list within a list.
lapply(n, function(i) {
list(
h5(paste0("Pan", i, " widgets")),
verbatimTextOutput(paste0('L2a', i)),
verbatimTextOutput(paste0('L2t', i)),
verbatimTextOutput(paste0('L2dat', i)),
br()
)
})
})
for (i in 1:TMAX) {
local({
my_i <- i
# Outputs
L2a <- paste0('L2a', my_i)
L2t <- paste0('L2t', my_i)
L2dat <- paste0('L2dat', my_i)
list(
output[[L2a]] <- renderPrint({ input[[paste0('L2amount', my_i)]] }),
output[[L2t]] <- renderPrint({ input[[paste0('L2type', my_i)]] }),
output[[L2dat]] <- renderPrint({ input[[paste0('L2daterange', my_i)]] })
)
})
}
}
shinyApp(ui, server)