I have 2 checkboxes and 1 action button. When clicking on either of the checkboxes, a graph should output BUT only after clicking on the action button. The code I have bellow does this well already. My issue here is that once the action button has been clicked and the graph generated, unclicking the checkbox removes the graph. Similarly, clicking again generates a new graph without clicking on the action button. I would like for the graph to stay on the screen for as long as I dont click on the action button again. I imagine this has to do with "isolating" the checkboxes but Im not too sure how to do so.
As a side note, imagine there was a third function generating a plot in my server when clicking on the action button (regardless of the checkboxes). Is there a way to code my "showmodal, removemodal" such that the pop up stays while all functions are running (instead of only during the first function)?
Here is my code
library(shiny)
#Function 1
X <- function(a,b,c){
plot(c(a,b),c(b,c))
}
#Function 2
Y <- function(d,e,f){
plot(c(d,e),c(e,f))
}
ui <- fluidPage(
titlePanel("title"),
sidebarLayout(
sidebarPanel(
checkboxInput("EF", "Efficient Frontier"),
checkboxInput("MonteCarlo", "Monte Carlo Simulation"),
actionButton("Go", "Go", style="color: #fff; background-color: #337ab7; border-color: #2e6da4; margin: auto")
),
mainPanel(
fluidRow(
align = "center",
conditionalPanel(condition = "input.EF == true", plotOutput("GraphEF")),
conditionalPanel(condition = "input.MonteCarlo == true", plotOutput("GraphMC"))
)
)
)
)
server <- function(input, output) {
OPw <- reactiveValues()
output$Graphw <- renderPlot({
OPw$PC}, height = 400, width = 400)
observeEvent(input$Go, {
showModal(modalDialog("Loading... Please Wait", footer=NULL))
output$GraphEF <- renderPlot({ #Efficient Frontier
if(input$EF){
X(5,10,15)
}
}, height = 550, width = 700)
output$GraphMC <- renderPlot({ #Monte Carlo Simulation
if(input$MonteCarlo){
Y(5,10,15)
}
},height = 550, width = 700)
removeModal() #Removes Loading Pop-up Message
})
}
shinyApp(ui = ui, server = server)
Thanks a lot for your help!
Perhaps you should use eventReactive(). Try this
library(shiny)
# Function 1
X <- function(a, b, c) {
plot(c(a, b), c(b, c))
}
# Function 2
Y <- function(d, e, f) {
plot(c(d, e), c(e, f))
}
ui <- fluidPage(
titlePanel("title"),
sidebarLayout(
sidebarPanel(
checkboxInput("EF", "Efficient Frontier"),
checkboxInput("MonteCarlo", "Monte Carlo Simulation"),
actionButton("Go", "Go", style = "color: #fff; background-color: #337ab7; border-color: #2e6da4; margin: auto")
),
mainPanel(
fluidRow(
align = "center",
uiOutput("plot1"),
plotOutput("GraphMC")
)
)
)
)
server <- function(input, output) {
GEF <- eventReactive(input$Go, {
if (input$EF) {
X(5, 10, 15)
} else {
NULL
}
})
showme <- eventReactive(input$Go, {
if (input$EF) TRUE else FALSE
})
GMC <- eventReactive(input$Go, {
if (isolate(input$MonteCarlo)) {
Y(5, 10, 15)
} else {
NULL
}
})
output$GraphMC <- renderPlot({
GMC()
})
output$GraphEF <- renderPlot({ # Efficient Frontier
GEF()
})
output$plot1 <- renderUI({
if (showme()) {plotOutput("GraphEF")} else NULL
})
observeEvent(input$Go, {
showModal(modalDialog("Loading... Please Wait", footer = NULL))
Sys.sleep(2)
removeModal() # Removes Loading Pop-up Message
})
}
shinyApp(ui = ui, server = server)
Leaving a conditionalPanel-approach, which is referring to a discussion over here:
library(shiny)
# Function 1
X <- function(a, b, c) {
plot(c(a, b), c(b, c))
}
# Function 2
Y <- function(d, e, f) {
plot(c(d, e), c(e, f))
}
ui <- fluidPage(
titlePanel("title"),
sidebarLayout(
sidebarPanel(
checkboxInput("EF", "Efficient Frontier"),
checkboxInput("MonteCarlo", "Monte Carlo Simulation"),
actionButton("Go", "Go", style = "color: #fff; background-color: #337ab7; border-color: #2e6da4; margin: auto")
),
mainPanel(
fluidRow(
align = "center",
conditionalPanel("output.showme == true", plotOutput("GraphEF")),
plotOutput("GraphMC")
)
)
)
)
server <- function(input, output) {
GEF <- eventReactive(input$Go, {
if (input$EF) {
X(5, 10, 15)
} else {
NULL
}
})
output$showme <- eventReactive(input$Go, {
if (input$EF) TRUE else FALSE
})
outputOptions(output, "showme", suspendWhenHidden = FALSE)
GMC <- eventReactive(input$Go, {
if (isolate(input$MonteCarlo)) {
Y(5, 10, 15)
} else {
NULL
}
})
output$GraphMC <- renderPlot({
GMC()
})
output$GraphEF <- renderPlot({ # Efficient Frontier
GEF()
})
observeEvent(input$Go, {
showModal(modalDialog("Loading... Please Wait", footer = NULL))
Sys.sleep(2)
removeModal() # Removes Loading Pop-up Message
})
}
shinyApp(ui = ui, server = server)
Furthermore, please see this related answer.
The modal is working well, because both functions take so little time to run it creates de sensation than is there less than it should be. We can show this by adding a sys.sleep to simulate a long calculation.
Regarding the checkboxes, using conditionalPanel will hide or show the plots independently of the presence of isolate inside the server. A workaround is just to return NULL when the checkbox is not clicked.
library(shiny)
# Function 1
X <- function(a, b, c) {
plot(c(a, b), c(b, c))
}
# Function 2
Y <- function(d, e, f) {
plot(c(d, e), c(e, f))
}
ui <- fluidPage(
titlePanel("title"),
sidebarLayout(
sidebarPanel(
checkboxInput("EF", "Efficient Frontier"),
checkboxInput("MonteCarlo", "Monte Carlo Simulation"),
actionButton("Go", "Go", style = "color: #fff; background-color: #337ab7; border-color: #2e6da4; margin: auto")
),
mainPanel(
fluidRow(
align = "center",
plotOutput("GraphEF"),
plotOutput("GraphMC")
)
)
)
)
server <- function(input, output) {
OPw <- reactiveValues()
output$Graphw <- renderPlot(
{
OPw$PC
},
height = 400,
width = 400
)
observeEvent(input$Go, {
showModal(modalDialog("Loading... Please Wait", footer = NULL))
output$GraphEF <- renderPlot(
{ # Efficient Frontier
if (isolate(input$EF)) {
X(5, 10, 15)
} else {
NULL
}
},
height = 550,
width = 700
)
Sys.sleep(2)
output$GraphMC <- renderPlot(
{ # Monte Carlo Simulation
if (isolate(input$MonteCarlo)) {
Y(5, 10, 15)
} else {
NULL
}
},
height = 550,
width = 700
)
removeModal() # Removes Loading Pop-up Message
})
}
shinyApp(ui = ui, server = server)
Related
I'm trying to set up a reactiveValues() object whose elements depends on a reactive() but I keep getting an error which says what I'm trying to do can only be done inside a reactive consumer. Here's a minimal code example:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
actionButton(inputId = "button1", "Generate"),
actionButton(inputId = "button2", "Toggle")
),
mainPanel(
verbatimTextOutput("toggle"),
verbatimTextOutput("dat_avail"),
verbatimTextOutput("test")
)
)
)
server <- function(input, output) {
toggle <- reactiveVal(FALSE)
dat_avail <- reactiveVal(FALSE)
observeEvent(input$button2, {
toggle(!toggle())
})
dat <- eventReactive(input$button1, {
x <- rnorm(10,0,1)
y <- rnorm(10,0,2)
data.frame(x,y)
})
observeEvent(input$button1, {
dat_avail(TRUE)
})
test <- reactiveValues({
if (toggle() & dat_avail()) {
m = mean(dat()$x)
}
else {
m = NULL
}
})
output$toggle <- renderPrint({toggle()})
output$dat_avail <- renderPrint({dat_avail()})
output$test <- renderPrint({test()})
}
shinyApp(ui = ui, server = server)
If I replace my test bit with the following, then it works:
test <- reactive({
if (toggle() & dat_avail()) {
mean(dat()$x)
}
else {
NULL
}
})
but I'd rather be able to do this with test as a reactiveValues() object instead. Is that doable?
EDIT:
Here's a more complicated setup where the answer below doesn't do what I intend it to do.
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
actionButton(inputId = "button1", "Generate"),
actionButton(inputId = "button2", "Toggle"),
sliderInput(inputId = "yrange",
label = "Range (y)",
min = -10,
max = 10,
value = c(-2,2))
),
mainPanel(
verbatimTextOutput("toggle"),
verbatimTextOutput("dat_avail"),
verbatimTextOutput("test"),
verbatimTextOutput("test2")
)
)
)
server <- function(input, output) {
toggle <- reactiveVal(FALSE)
dat_avail <- reactiveVal(FALSE)
observeEvent(input$button2, {
toggle(!toggle())
})
dat <- eventReactive(input$button1, {
x <- rnorm(100,0,1)
y <- rnorm(100,0,2)
data.frame(x,y)
})
new_dat <- reactive({
if (dat_avail()) {
subset(dat(), y<=input$yrange[2], y>=input$yrange[1])
}
else {
data.frame()
}
})
observeEvent(input$button1, {
dat_avail(TRUE)
})
test <- reactiveValues(m=NULL)
observeEvent(toggle(),{
test$m=
if (toggle() & dat_avail()) {
mean(dat()$x)
}
else {
NULL
}
})
test2 <- reactive({
if (toggle() & dat_avail()) {
mean(new_dat()$x)
}
else {
NULL
}
})
output$toggle <- renderPrint({toggle()})
output$dat_avail <- renderPrint({dat_avail()})
output$test <- renderPrint({test$m})
output$test2 <- renderPrint({test2()})
}
shinyApp(ui = ui, server = server)
test2 is what I'm after but I'd like to do it with reactiveValues() instead of reactive().
post the last edit :
observe({
test$m=
if (toggle() & dat_avail()) {
mean(new_dat()$x)
}
else {
NULL
}
})
I'm trying to do some integration testing on my Shiny app, but I don't know what I'm doing wrong. I suspect it has to do with the fact I used the "$" twice when trying to access the likelihood, color, and riskMessage variables.
I keep running into this error:
Error in checkEqualsNumeric(output$coloredBox$likelihoodOfHarm, 0.75) :
Modes: NULL, numeric
Lengths: 0, 1
target is NULL, current is numeric
Here is my Shiny app code, with my attempt to test the Shiny app at the bottom. Any help is greatly appreciated!
displayColoredBox<- function(color, riskMessage){
sidebarPanel(style=sprintf("background-color: %s; width: 300px; height: 300px;", color),
h3(sprintf("%s", riskMessage)) ) }
app <- shinyApp(
ui = fluidPage(
div(
id = "form",
sliderInput("count1", "First Slider Input", value=0, min=0, max=5000),
sliderInput("count2", "Second Slider Input", value=0, min=0, max=5000),
uiOutput("coloredBox")
)),
server <- function(input, output, session) {
output$coloredBox<-renderUI({
req(input$count1)
req(input$count2)
count1 <- input$count1;
count2 <- input$count2;
likelihood <- (count1*count2)/5000000
if (likelihood>1) {
color="red"
riskMessage="Extreme risk!"
} else if (likelihood>.65){
color="orange"
riskMessage="Very high risk!"
}
else if (likelihood>.35){
color="yellow"
riskMessage="High risk!"
}
else if (likelihood>.10){
color="blue"
riskMessage="Moderate risk!"
} else {
color="green"
riskMessage="Low risk!"
}
coloredBox=displayColoredBox(color, riskMessage)
})
}
)
testServer(app, {
session$setInputs(count1 = 1500)
session$setInputs(count2 = 2500)
checkEqualsNumeric(output$coloredBox$likelihood, 0.75)
checkEquals(output$coloredBox$riskMessage, "Very high risk!")
checkEquals(output$coloredBox$color, "orange")
})
output objects in Shiny Apps are not lists that you can access in the way you are trying to. In contrast, they are HTML objects. The shiny-way would be the following: Store your values as reactives that change according to the inputs. Check if the reactives have the desired values.
displayColoredBox<- function(color, riskMessage){
sidebarPanel(style=sprintf("background-color: %s; width: 300px; height: 300px;", color),
h3(sprintf("%s", riskMessage)) ) }
library(RUnit)
app <- shinyApp(
ui = fluidPage(
div(
id = "form",
sliderInput("count1", "First Slider Input", value=0, min=0, max=5000),
sliderInput("count2", "Second Slider Input", value=0, min=0, max=5000),
uiOutput("coloredBox")
)),
server <- function(input, output, session) {
likelihood <- reactive((input$count1*input$count2)/5000000)
boxValues <- reactiveValues(color="", riskMessage="")
observe({
req(input$count1)
req(input$count2)
if (likelihood()>1) {
boxValues$color="red"
boxValues$riskMessage="Extreme risk!"
} else if (likelihood()>.65){
boxValues$color="orange"
boxValues$riskMessage="Very high risk!"
}
else if (likelihood()>.35){
boxValues$color="yellow"
boxValues$riskMessage="High risk!"
}
else if (likelihood()>.10){
boxValues$color="blue"
boxValues$riskMessage="Moderate risk!"
} else {
boxValues$color="green"
boxValues$riskMessage="Low risk!"
}
})
output$coloredBox<-renderUI({
displayColoredBox(boxValues$color, boxValues$riskMessage)
})
}
)
testServer(app, {
session$setInputs(count1 = 1500)
session$setInputs(count2 = 2500)
checkEquals(likelihood(), 0.75)
checkEquals(boxValues$riskMessage, "Very high risk!")
checkEquals(boxValues$color, "orange")
})
I need help with the below shiny app server function. My problem is values$npv always comes out null, not even with a 0. and I think the fun function is not doing the right thing and i'm out of ideas.
If I hard-code the renderText with paste("Net Present Value:", isolate(input$val_inv)) i always have a result but not what i want and this makes me guess the fun function is not working as it should.
inline_numericInput=function(ni){
tags$div( class="form-inline",ni)
}
ui <- shinyUI(fluidPage(
tags$head(tags$style("#side_panel{
padding-left:10px;
}
.form-group {
margin-bottom: 15px !important;
}
.form-inline .form-control {
width:80%;
}
label{ width:30px;}
")),
titlePanel("Example"),
sidebarLayout(
sidebarPanel(width = 4,id="side_panel",
fluidRow(
column(6, inline_numericInput(numericInput("val_inv", label = "Inv:", value = 0))),
),
fluidRow(
column(6, inline_numericInput(numericInput("val_r", label = "R:", value = 0))),
),
fluidRow(
column(6, inline_numericInput(numericInput("val_n", label = "N:", min = 50,value = 50))),
column(6, inline_numericInput(actionButton("btn_calcnpv", label = "Compute NPV")))
)
),
mainPanel(
p('Results:'),
textOutput("val_npv")
)
)
))
server <- function(input, output) {
values <- reactiveValues()
values$npv <- 0
observe({
input$btn_calcnpv
fun <- function(n){
cf <- 0
for (i in 1:n){
cf <- cf + isolate(input$val_inv)/(1+input$var_r)**i
}
cf
}
values$npv <- fun(isolate(input$val_n))- isolate(input$val_inv)
#values$npv <- values$npv - isolate(input$val_inv)
})
output$val_npv <- renderText({
if(input$btn_calcnpv)
paste("Net Present Value:", values$npv)
else ""
})
}
shinyApp(ui, server)
Here is an answer using eventReactive and not so many isolations.
Furthermore, the inputs are coerced to numbers before the calculation takes place.
Using eventReactive, the calculation is started by pressing the compute-button.
server <- function(input, output) {
npv <- eventReactive(input$btn_calcnpv, {
val_inv <- as.numeric(input$val_inv)
val_r <- as.numeric(input$val_r)
val_n <- as.numeric(input$val_n)
fun <- function(n){
cf <- 0
for (i in 1:n){
cf <- cf + val_inv/(1+val_r)**i
}
cf
}
temp <- fun(val_n)- val_inv
temp
})
output$val_npv <- renderText({
req(npv())
paste("Net Present Value:", npv())
})
}
I was trying to switch the label of a show/hide columns button, and also keep the track of the number of times it is clicked in order to alter the number of columns showed of a table. I made it, but I couldn't use a direct even/odd differentiation of the value of the counter. Instead I had to use this: (vars$counter+1)/2) %% 2 == 0) to make it work, because each click changes the counter 2 times. I would like to request an easier procedure, maybe there is a shinyBS for that?
## app.R ##
library(shiny)
library(shinydashboard)
library(DT)
body<-dashboardBody(
textOutput("count"),
uiOutput('showallcolumnsbutton'),
DT::dataTableOutput('table2')
)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(),
body
)
server <- function(input, output) {
table<-data.frame(replicate(10,sample(0:1,1000,rep=TRUE)))
vars<-reactiveValues()
vars = reactiveValues(counter = 0)
observe({
if(!is.null(input$showallcolumns)){
input$showallcolumns
isolate({
vars$counter <- vars$counter + 1
})
}
})
label <- reactive({
if(!is.null(input$showallcolumns)){
if( ( (vars$counter+1)/2) %% 2 == 0) label <- "Hide"
else label <- "Show"
}
})
output$showallcolumnsbutton <- renderUI({
actionButton("showallcolumns", label = label(),
icon("hand-pointer-o"),
style="color: #000; background-color: #0099ff; border-color: #2e6da4"
)
})
output$count<-renderText({paste("counter value:",vars$counter)})
columnstoshow = reactive ({
x= ((vars$counter+1)/2) # %% 2 == 0)
if (!is.null (x))
{
if (x %% 2 == 0) {
c=c(1:10)
}
else {
c=c(1:5)
}
} #end 1st if
else {
c=c(1:10)
}
})
output$table2 = DT::renderDataTable({
DT::datatable(table[, columnstoshow()])
})
} # end server
shinyApp(ui, server)
Since Im not 100% what you want, is this it? Note that I used other library such as shinyBS
rm(list = ls())
library(shiny)
library(shinydashboard)
library(DT)
library(shinyBS)
body <- dashboardBody(bsButton("showallcolumns", label = "Hide", block = F, style="danger",icon=icon("hand-pointer-o")),br(),DT::dataTableOutput('table2'))
ui <- dashboardPage(dashboardHeader(),dashboardSidebar(),body)
server <- function(input, output,session) {
table <- data.frame(replicate(10,sample(0:1,1000,rep=TRUE)))
vars <- reactiveValues(counter = 1:10)
observeEvent(input$showallcolumns,{
if(input$showallcolumns %% 2){
updateButton(session, "showallcolumns",label = "Show", block = F, style = "success",icon=icon("hand-pointer-o"))
vars$counter <- 1:5
}
else{
updateButton(session, "showallcolumns",label = "Hide", block = F, style = "danger",icon=icon("hand-pointer-o"))
vars$counter <- 1:10
}
})
output$table2 = DT::renderDataTable({
DT::datatable(table[, vars$counter])
})
} # end server
shinyApp(ui, server)
I am trying to dynamically render multiple text output from multiple text input. I tried to use this very helpfull example and this one too.
This conversation is also helpfull.
But when I try to adapt this examples on the following script, I have a problem of output update. Apparently, only the last element was read and updated. It's probably a reactivity problem but it seems to be difficult to associate reactive{()} and renderUI{()}functions.
rm(list = ls())
library(shiny)
creatDataElem <- function(ne, input) {
x1 <- lapply(1:ne, function(i) {
textInput(paste0("elemName", i),
label = h4(strong("Name of dataset element")),
value = "")
})
return(x1)
}
ui = (fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("elemNb",
"Number of elements", value = 1, min = 1,
max = 3)
,
conditionalPanel(
condition = "input.elemNb == 1",
creatDataElem(1)
),
conditionalPanel(
condition = "input.elemNb == 2",
creatDataElem(2)
),
conditionalPanel(
condition = "input.elemNb == 3",
creatDataElem(3)
)
),
mainPanel(
uiOutput("nameElem")
)
)
)
)
server = function(input, output, session) {
max_elem <- 3
# Name
output$nameElem <-renderUI({
nameElem_output_list <- lapply(1:input$elemNb, function(i) {
elemName <- paste0("elemName", i)
tags$div(class = "group-output",
verbatimTextOutput(elemName)
)
})
do.call(tagList, nameElem_output_list)
})
for (i in 1:max_elem) {
local({
force(i)
my_i <- i
elemName <- paste0("elemName", my_i)
output[[elemName]] <- renderPrint(input[[elemName]])
})
}
}
runApp(list(ui = ui, server = server))
The idea with a reactive({}) function is to add an independant object (a function in this case) like:
nameElem <- reactive({
if (input$goElem == 0) {
return()
} else {
isolate({
if (is.null(input$elemName)) {
return()
} else if (test(input$elemName)) {
return("TEST RESULT")
} else {
return(input$elemName)
}
})
}
})
and to use renderUI on this object (with an ActionButton).
So, if someone knows why the output does not return the good object...
I think one of your problems is that your creatDataElem function is such that when it is called with argument ne=3, the first and second textInput elements are created again (and their value "lost").
Anyway, I think one solution would be to create those textInput elements as an "uiOutput".
You'll find a possible solution below which (I think) does what you want.
Lise
rm(list = ls())
library(shiny)
ui = (fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("elemNb",
"Number of elements", value = 1, min = 1,
max = 3),
uiOutput("myUI")
),
mainPanel(
uiOutput("nameElem")
)
)
)
)
server = function(input, output, session) {
output$myUI=renderUI({
w=""
for (i in 1:input$elemNb){
w=paste0(w,
textInput(paste0("elemName",i),label='Name of dataset element'))
}
HTML(w)
})
output$nameElem <-renderUI({
elems=c("<div>")
for(i in 1:input$elemNb){
elems=paste(elems,"</div><div>",input[[paste0("elemName",i)]])
}
elems=paste0(elems,"</div>")
HTML(elems)
})
}
runApp(list(ui = ui, server = server))
Found a solution:
library(readr)
library(dplyr)
library(shiny)
df <- data.frame(symbol = 1:10)
uiOutput("myUI")
createUI <- function(dfID, symbol) {
div(class="flex-box",paste0(symbol, " - 10"))
}
output$myUI <- renderUI({
w <- lapply(seq_len(nrow(df)), function(i) {
createUI(i, df[i,"symbol"])
})
do.call(fluidPage,w)
})