Shiny: reactiveValues() which depends on a reactive() - r

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
}
})

Related

In Shiny the output from the App file is not being showed once I send it to the module file

The I idea of my app is as follow:
Once I click on the First option of my select input it displays as options this names: names(mtcars)[1:4], if I click on the second option it displays these options: c('Option A','Option B','Option C','Option D').
And then if I click on those names links, bellow is displayed ther names.
The problem:
From the starts it doesnt work well. I receive this warning message:
Warning: Error in as.vector: cannot coerce type 'environment' to vector of type 'character'
But once I start to use the App everyhthing works fine.
Can you help me to fix this app?
This is the main app file:
ui <- fluidPage(
selectInput(inputId = 'selection',
choices = c('First Option','Second Option'),
label = 'Select'),
htmlOutput('options_choice'),
example_UI('example')
)
server <- function(input, output, session) {
observeEvent(input$selection,
if(input$selection == 'First Option'){
output$options_choice <- renderUI({
names(mtcars)[1:4] %>% map(~ actionLink(label = paste0(.x),
inputId = paste0(.x)))
})
}else{
output$options_choice <- renderUI({
c('Option A','Option B','Option C','Option D') %>% map(~ actionLink(label = paste0(.x),
inputId = paste0(.x)))
})
})
name <- reactiveVal(observeEvent(input$selection, {
if (input$selection == 'First Option') {
"mpg"
} else{
"Option A"
}
}))
observeEvent(input$selection,{
if(input$selection == 'First Option'){
names(mtcars)[1:4] %>% map(~ observeEvent(input[[.x]],
{
name(.x)
}))
}else{
c('Option A','Option B','Option C','Option D') %>% map(~ observeEvent(input[[.x]],
{
name(.x)
}))
}
})
example_Server("example", names = name)
}
shinyApp(ui, server)
And here is the module file:
example_UI <- function(id) {
ns <- NS(id)
tagList(
htmlOutput(ns('name_from_main'))
)
}
example_Server <- function(id, names) {
moduleServer(
id,
function(input, output, session) {
output$name_from_main <- renderUI({
h1(names())
})
}
)
}
Any help would be amazing
Perhaps you are looking for this
example_UI <- function(id) {
ns <- NS(id)
tagList(
htmlOutput(ns('name_from_main'))
)
}
example_Server <- function(id, names) {
moduleServer(
id,
function(input, output, session) {
output$name_from_main <- renderUI({
h1(names())
})
}
)
}
ui <- fluidPage(
selectInput(inputId = 'selection',
choices = c('First Option','Second Option'),
label = 'Select'),
htmlOutput('options_choice'),
example_UI('example')
)
server <- function(input, output, session) {
rv <- reactiveValues(names=NULL, name=NULL)
observeEvent(input$selection, {
if(input$selection == 'First Option') {
rv$names = names(mtcars)[1:4]
}else rv$names = c('Option A','Option B','Option C','Option D')
})
output$options_choice <- renderUI({
input$selection
rv$names %>% map(~ actionLink(label = paste0(.x), inputId = paste0(.x)))
})
observe({
if (input$selection == 'First Option') {
rv$name = "mpg"
} else{
rv$name = "Option A"
}
})
observeEvent(input$selection, {
lapply(rv$names, function(x){
observeEvent(input[[x]], {
rv$name = as.character(x)
})
})
})
example_Server("example", names = reactive(rv$name))
}
shinyApp(ui, server)

How to render a list of dataframes as tables to show as output in Shiny

I am working in a shiny app to compare multiple items according to an input defined by the user. The code works fine but I have an issue. I do not know what function I should apply in order to display the results of some computing as tables in the right side of the app. The code of the app is next:
library(shiny)
library(shinydashboard)
#Function
compute <- function(firstitem,seconditem)
{
Sum <- firstitem+seconditem
Difference <- firstitem+seconditem
Product <- firstitem*seconditem
Ratio <- firstitem/seconditem
Res <- data.frame(C1=Sum,C2=Difference,C3=Product,C4=Ratio)
return(Res)
}
#App
ui = shinyUI(fluidPage(
titlePanel("Compare"),
sidebarLayout(
sidebarPanel(
numericInput("numitems", label = "Number of items to compare?",
min = 1, max = 5, value = 1),
uiOutput("period_cutpoints"),
uiOutput("period_cutpoints2"),
actionButton("submit", "Submit")
),
mainPanel(
textOutput("numitems"),
textOutput("cutpoints")
)
)
))
server = shinyServer(function(input, output, session) {
output$period_cutpoints<-renderUI({
req(input$numitems)
lapply(1:(input$numitems), function(i) {
numericInput(inputId=paste0("firstitem",i),
label=paste0("Enter the value of first item ", i, ":"),value = 0)
})
})
output$period_cutpoints2<-renderUI({
req(input$numitems)
lapply(1:(input$numitems), function(i) {
numericInput(inputId=paste0("seconditem",i),
label=paste0("Enter the value of second item ", i, ":"),value = 0)
})
})
seldates <- reactiveValues(x=NULL)
observeEvent(input$submit, {
seldates$x <- list()
lapply(1:(input$numitems), function(i) {
seldates$x[[i]] <- compute(firstitem = input[[paste0("firstitem", i)]],seconditem = input[[paste0("seconditem", i)]])
})
})
output$cutpoints <- renderText({as.character(seldates$x)})
})
shinyApp(ui = ui, server = server)
It is working but my issue is that I do not know how to set the content of seldates, which are dataframes, as tables that should appear one after another. This task is done with output$cutpoints but I can not get them as Tables:
Does anybody know how can I fix this issue? Many thanks!
Try this
library(shiny)
library(shinydashboard)
library(DT)
#Function
compute <- function(firstitem,seconditem)
{
Sum <- firstitem+seconditem
Difference <- firstitem+seconditem
Product <- firstitem*seconditem
Ratio <- firstitem/seconditem
Res <- data.frame(C1=Sum,C2=Difference,C3=Product,C4=Ratio)
return(Res)
}
#App
ui = shinyUI(fluidPage(
titlePanel("Compare"),
sidebarLayout(
sidebarPanel(
numericInput("numitems", label = "Number of items to compare?",
min = 1, max = 5, value = 1),
uiOutput("period_cutpoints"),
uiOutput("period_cutpoints2"),
actionButton("submit", "Submit")
),
mainPanel(
textOutput("numitems"),
textOutput("cutpoints"),
uiOutput("t1")
)
)
))
server = shinyServer(function(input, output, session) {
output$period_cutpoints<-renderUI({
req(input$numitems)
lapply(1:(input$numitems), function(i) {
numericInput(inputId=paste0("firstitem",i),
label=paste0("Enter the value of first item ", i, ":"),value = i)
})
})
output$period_cutpoints2<-renderUI({
req(input$numitems)
lapply(1:(input$numitems), function(i) {
numericInput(inputId=paste0("seconditem",i),
label=paste0("Enter the value of second item ", i, ":"),value = i+i)
})
})
seldates <- reactiveValues(x=NULL)
observeEvent(input$submit, {
seldates$x <- list()
lapply(1:(input$numitems), function(i) {
seldates$x[[i]] <- compute(firstitem = input[[paste0("firstitem", i)]],seconditem = input[[paste0("seconditem", i)]])
})
})
output$cutpoints <- renderText({as.character(seldates$x)})
observeEvent(input$submit, {
lapply(1:(input$numitems), function(i) {
output[[paste0("table",i)]] <- renderDT(seldates$x[[i]])
})
output$t1 <- renderUI({
tagList(
lapply(1:(input$numitems), function(i) {
DTOutput(paste0("table",i))
})
)
})
})
})
shinyApp(ui = ui , server = server)

R Shiny: How to use conditions within main server function to call different module UI/server functions?

I am trying to use if/then construct in main server function to determine which, out of a choice of two, modules to call, depending on user input. One choice uses an add/remove action button module to call another module, the other choice calls a different module not using the add/remove button module. Calling the module using add/ remove module is easy enough, as I am passing the UI to call as one of the parameters in the call to the add/remove button module, but I am not sure how to properly insertUI() in the server function after callModule(). So in my example (as simple as I could think to make it), the UI starts with a textInput() box, which defaults to 1. I have a "first" module, which just prepends the userInput() data to letters a,b,c d in selectInput() box. The "second" module prepends "Not 1" to a,b,c,d in selectInput() box. I use observeEvent({}) such that if user does nothing (textInput() stays at 1), then "first" module is called. If the user changes textInput() to anything at all other than default 1, "second" module is called. What I am not clear on is how to call the UI for the second module. I have a uiOutput("dummy") as a placeholder in the ui() function. However, my example does not work as described above, because it does not ever successfully call "second" module if the user changes the testInput() default value. Code below, thanks!
library(shiny)
firstUI <- function(id) { uiOutput(NS(id, "first")) }
firstServer <- function(input, output, session, a) {
ns = session$ns
output$first <- renderUI({
selectInput(ns("select"), h4("Select"), paste0(isolate(a()), letters[1:4]))
})
return(reactive({ c(paste0(input$select), paste0(input$select)) }))
}
removeFirstUI <- function(id) {
removeUI(selector = paste0('#', NS(id, "first")))
}
secondUI <- function(id) { uiOutput(NS(id, "second")) }
secondServer <- function(input, output, session, a) {
ns = session$ns
output$second <- renderUI({
selectInput(ns("select"), h4("Select"), paste0("Not 1", letters[1:4]))
})
return(reactive({ c(paste0(input$select), paste0(input$select)) }))
}
removeSecondUI <- function(id) {
removeUI(selector = paste0('#', NS(id, "second")))
}
addRmBtnUI <- function(id) {
ns <- NS(id)
tags$div(
actionButton(inputId = ns('insertParamBtn'), label = "Add"),
actionButton(ns('removeParamBtn'), label = "Remove"),
hr(),
tags$div(id = ns('placeholder'))
)
}
addRmBtnServer <- function(input, output, session, moduleToReplicate, ...) {
ns = session$ns
params <- reactiveValues(btn = 0, a = list())
observeEvent(input$insertParamBtn, {
params$btn <- params$btn + 1
params$a[[params$btn]] <- callModule(moduleToReplicate$server, id = params$btn, ...)
insertUI(
selector = paste0('#', ns('placeholder')),
ui = moduleToReplicate$ui(ns(params$btn))
)
})
observeEvent(input$removeParamBtn, {
moduleToReplicate$remover(ns(params$btn))
params$btn <- params$btn - 1
})
return(params)
}
ui <- fluidPage(
addRmBtnUI("addRm"),
textInput("a", label = "a", value = 1, width = '150px'),
verbatimTextOutput("text", placeholder = TRUE),
uiOutput("dummy")
)
server <- function(input, output, session) {
a <- reactive({ input$a })
comp <- reactiveValues()
observeEvent(a(), {
if (input$a == 1) {
comp <- callModule(
addRmBtnServer, id = "addRm",
moduleToReplicate = list(
ui = firstUI,
server = firstServer,
remover = removeFirstUI
),
a = a
)
} else {
comp <- callModule(
secondServer, id = 0,
a = a
)
}
}, ignoreNULL = TRUE)
output$text <- renderPrint({
if (!(is.null(comp$btn))) {
if (comp$btn > 0) {
paste(
comp$a[[comp$btn]](),
sep = " "
)
}
} else { paste0("") }
})
}
shinyApp(ui = ui, server = server)

Summing the values entered in textInput in RShiny

I am developing the Shiny app and I am unable to sum the values entered in dynamically created textInput.
The RCode used is as follows:
ui <- fluidPage(
fluidRow(
column(3, offset = 3,wellPanel(textOutput("text2"))),
column(3,wellPanel(textOutput("text3"))),
column(3,wellPanel(textOutput("text4")))
)
)
server <- function(input, output, session){
observeEvent(input$view, {
output$inputGroup = renderUI({
#code for generating textBoxes and corresponding Id's dynamically
input_list <- lapply(1:(nrow(df())*3), function(i) {
inputName <- paste("id", i, sep = "")
textInputRow<-function (inputId,value)
{
textAreaInput(inputName,"", width = "200px", height = "43px", resize = "horizontal")
}
column(4,
textInputRow(inputName, "")
)
})
do.call(tagList, input_list)
})
})
#code for adding the values and displaying the sum
output$text2 <- renderText({
tot = nrow(df())*3
sum1 = 0
for(lim in 1:tot){
if(lim %% 3 == 1){
inp = paste("id",lim)
sum1 = sum1 + input[[inp]]
}
}
})
}
shinyApp(ui = ui, server = server)
The output is :
Can anyone help me with this code?
While your question is modified, Here's a reproducible code for summing values entered in the textbox:
## Only run examples in interactive R sessions
if (interactive()) {
ui <- fluidPage(
textInput("input1", "Input1", 1),
textInput("input2", "Input2", 2),
tags$h3('Result:'),
verbatimTextOutput("value")
)
server <- function(input, output) {
output$value <- renderText({ as.numeric(input$input1) + as.numeric(input$input2)})
}
shinyApp(ui, server)
}

observeEvent brush within a reactive event in RShiny

I am showing tabbed graphs for each line that is selected in my initial table. I would like those graphs to have the brush/zoom functionality found here.
Here is my code :
library(shiny)
library(DT)
library(ggplot2)
library(scales)
library(reshape2)
First the ui : the main table with a tabbed UI below that is generated in response to selection of rows in the main table
ui <- fluidPage(
mainPanel(
fluidRow(
column(12,DT::dataTableOutput(outputId = 'tableCurrencies'))
),
fluidRow(
uiOutput("selectedTabs")
)
)
)
Then the server function : the main table values are generated randomly for the sake of the example. The brush functionality is directly lifted from the link provided. I suspect my issue has to do with a reactive function within a reactive function but I'm happy to let the experts decide.
server <- function(input,output){
output$tableCurrencies <- DT::renderDataTable({datatable(data.frame(a=rnorm(10),b=rnorm(10),c=rnorm(10)))})
origTable_selected <- reactive({
ids <- input$tableCurrencies_rows_selected
return(ids)
})
rangeRates <- reactiveValues(xRate = NULL, yRate = NULL)
output$selectedTabs <- renderUI({
myTabs <- lapply(origTable_selected(),function(i) {
tabName <- paste0("test",i)
a <- renderPlot({
hist(rnorm(50))
})
output[[paste0(tabName,"rates")]] <- a
#plot of realized vol and implied vols over 5 years
observeEvent(input[[paste0(tabName,"rates_dblclick")]], {
brush <- input[[paste0(tabName,"rates_brush")]]
if (!is.null(brush)) {
rangeRates$xRate <- c(brush$xmin, brush$xmax)
rangeRates$yRate <- c(brush$ymin, brush$ymax)
} else {
rangeRates$xRate <- NULL
rangeRates$yRate <- NULL
}
})
return(tabPanel(
tabName,
fluidRow(
column(6,plotOutput(paste0(tabName,"rates")))
)
))
})
return(do.call(tabsetPanel,myTabs))
})
}
app = shinyApp(ui,server)
runApp(app,port=3250,host='0.0.0.0')
You need to assign the "doubleclick id" and the "brush id" in the plotOutput call
column(6, plotOutput(paste0(tabName, "rates"),
dblclick = paste0(tabName, "rates_dblclick"),
brush = brushOpts(
id = paste0(tabName, "rates_brush"),
resetOnNew = TRUE
)))
Now the observers trigger properly and send the right information. There is still a second issue with rangeRates not having any effect on the plots which can be solved the following way
a <- renderPlot({
if (!is.null(rangeRates$xRate))
hist(rnorm(50), xlim = rangeRates$xRate,
ylim = rangeRates$yRate)
else
hist(rnorm(50))
})
Here is the full working version
library(shiny)
library(DT)
ui <- fluidPage(
mainPanel(
fluidRow(column(12, DT::dataTableOutput(outputId = 'tableCurrencies'))),
fluidRow(uiOutput("selectedTabs"))
)
)
server <- function(input, output){
output$tableCurrencies <- DT::renderDataTable({
data.frame(a = rnorm(10), b = rnorm(10), c = rnorm(10))
})
origTable_selected <- reactive({
ids <- input$tableCurrencies_rows_selected
return(ids)
})
rangeRates <- reactiveValues(xRate = NULL, yRate = NULL)
output$selectedTabs <- renderUI({
myTabs <- lapply(
origTable_selected(),
function(i) {
tabName <- paste0("test", i)
output[[paste0(tabName, "rates")]] <- renderPlot({
if( !is.null(rangeRates$xRate) )
hist(rnorm(50), xlim = rangeRates$xRate,
ylim = rangeRates$yRate)
else
hist(rnorm(50))
})
observeEvent(input[[paste0(tabName, "rates_dblclick")]], {
brush <- input[[paste0(tabName, "rates_brush")]]
if (!is.null(brush)) {
rangeRates$xRate <- c(brush$xmin, brush$xmax)
rangeRates$yRate <- c(brush$ymin, brush$ymax)
} else {
rangeRates$xRate <- NULL
rangeRates$yRate <- NULL
}
})
tabPanel(
tabName,
fluidRow(column(6, plotOutput(
paste0(tabName, "rates"),
dblclick = paste0(tabName, "rates_dblclick"),
brush = brushOpts(
id = paste0(tabName, "rates_brush"),
resetOnNew = TRUE)
)))
)
})
return(do.call(tabsetPanel, myTabs))
})
}
shinyApp(ui, server)

Resources