How to count number of times the selectInput box is clicked? - r

The below MWE code uses observeEvent() functions and reactiveVal() to track and show the number of times the selectInput() box is clicked and the number of times the actionButton() is clicked.
The actionButton() tracking works fine. Note how in output$... in the code the selectInput() and actionButton() work differently, looks weird to me.
Anyhow, I'm having trouble having selectInput() track the same as actionButton(). When the App is first invoked, the user clicking the first option rendered ("Cyl") in the selectInput() box is not counted as a click, when I would like it to count as a click. And if the same choice is clicked in the selectInput() ("Trans" for example) more than once, the clicks > 1 aren't counted as clicks when I would each click to be counted. Basically, anytime the user clicks in the selectInput() box it needs to be included as a "click". Is there any way to do this?
In the full code this matters because the selectInput() in that box triggers a removeUI() and the list of choices is dynamic and sequentially renumbered with every click.
Code:
library(shiny)
ui = fluidPage(hr(),
selectInput("selInput",label=NULL,c("Cyl"="cyl","Trans"="am","Gears"="gear"),selected=NULL),
actionButton("addBtn","Add"), hr(),
textOutput("clickSelInput"),
textOutput("clickAddBtn"),
tableOutput("data")
)
server = function(input, output) {
x = reactiveVal(0)
y = reactiveVal(0)
output$data <- renderTable({mtcars[1:10, c("mpg", input$selInput), drop = FALSE]})
observeEvent(input$selInput,{x(x()+1)})
observeEvent(input$addBtn,{y(y()+1)})
output$clickSelInput <- renderText({paste('Select Input clicks =',x()-1)})
output$clickAddBtn <- renderText({paste('Add Button clicks =',y())})
}
shinyApp(ui, server)

Adding "multiple = TRUE" to the selectInput() in this case resolves the question. Also allow the removal of the weird -1 from the out$selInput..x()-1)}) in the OP. This also works fine for the larger App this is intended for. See revised OP code with changes from OP commented (further down is the "larger App" where this functionality matters):
library(shiny)
ui = fluidPage(hr(),
selectInput("selInput",
label=NULL,
c("Cyl"="cyl","Trans"="am","Gears"="gear"),
selected=NULL,
multiple=TRUE # added this
),
actionButton("addBtn","Add"), hr(),
textOutput("clickSelInput"),
textOutput("clickAddBtn"),
tableOutput("data")
)
server = function(input, output) {
x = reactiveVal(0)
y = reactiveVal(0)
output$data <- renderTable({mtcars[1:10, c("mpg", input$selInput), drop = FALSE]})
observeEvent(input$selInput,{x(x()+1)})
observeEvent(input$addBtn,{y(y()+1)})
output$clickSelInput <- renderText({paste('Select Input clicks =',x())}) # removed the -1 from x()
output$clickAddBtn <- renderText({paste('Add Button clicks =',y())})
}
shinyApp(ui, server)
And here's the "larger App" where this functionality matters:
library(dplyr)
library(rhandsontable)
library(shiny)
rowNames1 <- c("A", "B", "C", "Sum")
DF1 <- data.frame(row.names = rowNames1, "Col 1" = c(1, 1, 0, 2), check.names = FALSE)
ui <- fluidPage(br(),
rHandsontableOutput('hottable1'),br(),
actionButton("addCol1", "Add column 1"),br(),
h5(strong("Select column to delete:")),
uiOutput("delCol1"), hr(),
textOutput("clickSelInput"),
textOutput("clickAddBtn"),
)
server <- function(input, output) {
x = reactiveVal(0)
y = reactiveVal(0)
uiTbl1 <- reactiveVal(DF1)
observeEvent(input$hottable1, {uiTbl1(hot_to_r(input$hottable1))})
output$hottable1 <- renderRHandsontable({
rhandsontable(uiTbl1(),rowHeaderWidth = 100, useTypes = TRUE)%>%
hot_context_menu(allowRowEdit = FALSE, allowColEdit = FALSE)
})
observeEvent(input$addCol1, {
newCol <- data.frame(c(1,1,0,2))
names(newCol) <- paste("Col", ncol(hot_to_r(input$hottable1)) + 1)
uiTbl1(cbind(uiTbl1(), newCol))
})
observeEvent(input$delCol1, {
tmp <- uiTbl1()
delCol <- input$delCol1
tmp <- tmp[ , !(names(tmp) %in% delCol), drop = FALSE]
newNames <- sprintf("Col %d",seq(1:ncol(tmp)))
names(tmp) <- newNames
uiTbl1(tmp)
})
output$delCol1 <-
renderUI(
selectInput(
"delCol1",
label = NULL,
choices = colnames(hot_to_r(input$hottable1)),
selected = "",
multiple = TRUE)
)
observeEvent(input$delCol1,{x(x()+1)})
observeEvent(input$addCol1,{y(y()+1)})
output$clickSelInput <- renderText({paste('Select Input clicks =',x())})
output$clickAddBtn <- renderText({paste('Add Button clicks =',y())})
}
shinyApp(ui,server)

Related

In R Shiny, how to dynamically expand the use of a function as user inputs expand?

The following MWE code interpolates user inputs (Y values in 2-column matrix input grid in sidebar panel, id = input1) over X periods (per slider input in sidebar, id = periods). The custom interpolation function interpol() is triggered in server section by results <- function(){interpol(...)}. User has the option to add or modify scenarios by clicking on the single action button, which triggers a modal housing a 2nd expandable matrix input (id = input2). Interpolation results are presented in the plot in the main panel. So far so good, works as intended.
As drafted, the results function only processes the first matrix input including any modifications to it executed in the 2nd matrix input.
My question: how do I expand the results function to include scenarios > 1 added in the 2nd expandable matrix input, and automatically include them in the output plot? I've been wrestling with a for-loop to do this but don't quite know how. I've avoided for-loops, instead relying on lapply and related. But in practice a user will not input more than 20-30 scenarios max and perhaps a for-loop is a harmless option. But I'm open to any solution and am certainly not wedded to a for-loop!
MWE code:
library(shiny)
library(shinyMatrix)
interpol <- function(a,b){ # a = periods, b = matrix inputs
c <- rep(NA,a)
c[1] <- b[1]
c[a] <- b[2]
c <- approx(seq_along(c)[!is.na(c)],c[!is.na(c)],seq_along(c))$y # this interpolates
return(c)
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(uiOutput("panel"),actionButton("showInput2","Modify/add interpolation")),
mainPanel(plotOutput("plot1"))
)
)
server <- function(input, output, session){
results <- function(){interpol(req(input$periods),req(input$input1))}
output$panel <- renderUI({
tagList(
sliderInput('periods','Interpolate over periods (X):',min=2,max=12,value=6),
uiOutput("input1"))
})
output$input1 <- renderUI({
matrixInput("input1",
label = "Interpolation 1 (Y values):",
value = matrix(if(isTruthy(input$input2)){c(input$input2[1],input$input2[2])}
else {c(1,5)}, # matrix values
1, 2, # matrix row/column count
dimnames = list(NULL,c("Start","End")) # matrix column header
),
rows = list(names = FALSE),
class = "numeric")
})
observeEvent(input$showInput2,{
showModal(
modalDialog(
matrixInput("input2",
label = "Automatically numbered scenarios (input into blank cells to add):",
value = if(isTruthy(input$input2)){input$input2}
else if(isTruthy(input$input1)){input$input1},
rows = list(names = FALSE),
cols = list(extend = TRUE,
delta = 2,
delete = TRUE,
multiheader=TRUE),
class = "numeric"),
footer = modalButton("Close")
))
})
observe({
req(input$input2)
mm <- input$input2
colnames(mm) <- paste(trunc(1:ncol(mm)/2)+1, " (start|end)")
isolate(updateMatrixInput(session, "input2", mm))
})
output$plot1 <-renderPlot({
req(results())
plot(results(),type="l", xlab = "Periods (X)", ylab = "Interpolated Y values")
})
}
shinyApp(ui, server)
As a user can (presumably) add only one scenario at a time, I don't think a for loop is going to help. The way I handle situations like this is to bind additional data to the appropriate reactive in an observeEvent. This will then trigger updates in the necessary outputs automatically. Here's a MWE to illustrate the technique.
library(shiny)
library(tidyverse)
ui <- fluidPage(
actionButton("add", "Add scenario"),
plotOutput("plot"),
)
server <- function(input, output, session) {
v <- reactiveValues(results=tibble(Scenario=1, X=1:10, Y=runif(10)))
observeEvent(input$add, {
newData <- tibble(Scenario=max(v$results$Scenario) + 1, X=1:10, Y=runif(10))
v$results <- v$results %>% bind_rows(newData)
})
output$plot <- renderPlot({
v$results %>% ggplot() + geom_point(aes(x=X, y=Y, colour=as.factor(Scenario)))
})
}
shinyApp(ui, server)

How to use action buttons to show and hide table output in R shiny?

In the below simple App code, I generate a user input table (or matrix) inside a Shiny modal dialog. Clicking the "Modify" action button pulls up a default user input table where the user can modify the default values, insert/delete input columns, etc. "Show" action button pulls up table2 in the main page, "Hide" hides that same table. (You can ignore the table1 that appears in the modal box, that's temporarily there for testing purposes, to be deleted later). "Reset" button reverts the table back to the default table.
Problem with this is "Show" and "Hide" work only once. Also, after having modified the input table (or matrix), clicking "Modify" pulls up the default table and not the most recently modified table.
So, how would I modify the below so that (i) clicking "Show" and "Hide" respectively show and hide the most recently modified table, repeatedly (OK to have a combined Show/Hide button too, using shinyjs toggle function, something I have toyed with), (ii) clicking "Modify" the first time the App is invoked pulls up the default table (as it currently does) but subsequent clicks of "Modify" pull up the most recently modified table, and (iii) clicking "Show" without having first modified the table pulls up the default table?
MWE code:
library(shiny)
library(shinyMatrix)
library(shinyjs)
matrix3Input <- function(x){
matrixInput(x,
label = 'Series terms:',
value = matrix(c(1,24,0,1),4,1,dimnames=list(c("A","B","C","D"),NULL)),
rows = list(extend = FALSE,names = TRUE),
cols = list(extend = TRUE,names = TRUE,editableNames = TRUE,delete = TRUE),
class = "numeric") # close matrix input
} # close function
ui <- fluidPage(
useShinyjs(),
titlePanel("Inputs"),
fluidRow(actionButton("modify","Modify"),
actionButton("show","Show"),
actionButton("hide","Hide"),
actionButton("reset","Reset"),
tableOutput("table2")
) # close fluid row
) # close fluid page
server <- function(input, output, session) {
observeEvent(input$modify,{showModal(modalDialog(
matrix3Input("matrix"),
tableOutput("table1"))
)})
output$table1 <- renderTable(input$matrix, rownames = TRUE)
observeEvent(input$show,{
tableOutput("table2")
output$table2 <- renderTable(input$matrix, rownames = TRUE)
})
observeEvent(input$hide,{hide("table2")})
observeEvent(input$reset,{
tableOutput("table2")
output$table2 <- renderTable(input$matrix, rownames = TRUE)
})
} # close server
shinyApp(ui, server)
I think this should cover for all the different scenarios.
I have used reactiveValues to save matrix3Input and matrix.
library(shiny)
library(shinyMatrix)
library(shinyjs)
default_mat <- matrix(c(1,24,0,1),4,1,dimnames=list(c("A","B","C","D"),NULL))
matrix3Input <- function(x, default_mat){
matrixInput(x,
label = 'Series terms:',
value = default_mat,
rows = list(extend = FALSE,names = TRUE),
cols = list(extend = TRUE,names = TRUE,editableNames = TRUE,delete = TRUE),
class = "numeric") # close matrix input
} # close function
ui <- fluidPage(
useShinyjs(),
titlePanel("Inputs"),
fluidRow(actionButton("modify","Modify"),
actionButton("show","Show"),
actionButton("hide","Hide"),
actionButton("reset","Reset"),
tableOutput("table2")
) # close fluid row
) # close fluid page
server <- function(input, output, session) {
rv <- reactiveValues(mat = matrix3Input("matrix", default_mat), input = default_mat)
hide("table2")
observeEvent(input$modify,{
showModal(modalDialog(
rv$mat,
tableOutput("table1"))
)
hide("table2")
})
output$table1 <- renderTable({
rv$mat <- matrix3Input("matrix", input$matrix)
rv$input <- input$matrix
input$matrix
}, rownames = TRUE)
observeEvent(input$show,{
show("table2")
})
observeEvent(input$hide, hide("table2"))
observeEvent(input$reset,{
hide("table2")
rv$input <- default_mat
rv$mat <- matrix3Input("matrix", default_mat)
})
output$table2 <- renderTable({
rv$input
}, rownames = TRUE)
} # close server
shinyApp(ui, server)

R Shiny Application Conditional calculations and panel with condition on the output

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".

R Shiny: initial rendering of eventReactive output

What I'm trying to achieve is to have an initial table to be rendered right as the app is executed. But then, update the table only on executing action.
Here's the example:
library(shiny)
library(data.table)
dt <- data.table(x = c("a", "b"), y = c(0,0))
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(inputId = "inSelect",
label = "Select:",
choices = dt[,unique(x)]),
actionButton(inputId = "trigger",
label = "Trigger",
icon = icon("refresh"))
),
mainPanel(
tableOutput("outTable")
)
)
)
server <- function(input, output){
re <- eventReactive(input$trigger, {
dt[x == input$inSelect, y := y + 1]
})
output$outTable <- renderTable({
re()
})
}
shinyApp(ui, server)
So the issue is that under renderTable() I can put either dt to show initial table or re() to show each update after first press of the "Trigger" button.
Do
re <- eventReactive(input$trigger, {
dt[x == input$inSelect, y := y + 1]
}, ignoreNULL = FALSE)
From ?eventReactive:
Both observeEvent and eventReactive take an ignoreNULL parameter that
affects behavior when the eventExpr evaluates to NULL (or in the
special case of an actionButton, 0).

Showing and hiding inputs based on checkboxGroupInput

My shiny app begins with a checkboxGroupInput which contains the names of three companies: A, B and C. It also has 3 hidden numeric inputs, each corresponding to a company. Potential investors may select the name of the company they wish to invest in and specifiy the amount they are willing to invest. When the name of a company is checked, the corresponding numeric input shows up. Also, when the company name is unchecked, the numeric input disappears.
The checkboxGroupInput is called company. The 3 numericInput fields are respectively called amountA, amountB and amountC and are all generated inside a uiOutput. They are hidden with the hidden function of shinyjs.
library(shiny)
library(shinyjs)
library(magrittr)
ui <- fluidPage(
useShinyjs(),
checkboxGroupInput(inputId = "company", label = "Select a company", choices = LETTERS[1:3]),
uiOutput(outputId = "amounts")
)
server <- function(input, output){
company_names <- LETTERS[1:3]
num_ids <- paste0("amount", LETTERS[1:3])
output$amounts <- renderUI({
num_inputs <- lapply(1:3, function(i){
numericInput(inputId = num_ids[i], label = paste0("Investment in ", company_names[i]), value = 0, min = 0, max = 5000)
}) %>% tagList
shinyjs::hidden(num_inputs)
})
observeEvent(eventExpr = input$company, handlerExpr = {
if(length(input$company) == 0){
for(i in num_ids){
shinyjs::hide(id = i)
}
} else {
for(i in input$company){
shinyjs::toggle(id = paste0("amount", i), condition = input$company)
}
}
})
}
shinyApp(ui = ui, server = server)
The problem with my app is that the intended dynamics between the checkboxGroupInput and the numericInput fields are not working as intended. For instance, once a numericInput is shown, it cannot be hidden anymore by unchecking the boxes. How can I handle this?
The code pasted above is fully functional. Thank you very much.
I fixed your code by explicitly show/hide the numericInput when the corresponding check box is selected/unselected. Also I change the observeEvent with observe to make sure that the observer reacts when none of the check boxes are selected.
library(shiny)
library(shinyjs)
library(magrittr)
ui <- fluidPage(
useShinyjs(),
checkboxGroupInput(inputId = "company", label = "Select a company", choices = LETTERS[1:3]),
uiOutput(outputId = "amounts")
)
server <- function(input, output){
company_names <- LETTERS[1:3]
num_ids <- paste0("amount", LETTERS[1:3])
output$amounts <- renderUI({
num_inputs <- lapply(1:3, function(i){
numericInput(inputId = num_ids[i], label = paste0("Investment in ", company_names[i]), value = 0, min = 0, max = 5000)
}) %>% tagList
shinyjs::hidden(num_inputs)
})
observe({
for(i in company_names){
if (i %in% input$company) {
shinyjs::show(id = paste0("amount", i))
} else {
shinyjs::hide(id = paste0("amount", i))
}
}
})
}
shinyApp(ui = ui, server = server)

Resources