How to make actionbutton only work every time it is pressed? - r

Hello and thanks for reading me. I am currently trying to make a simple app in shiny that allows you to filter a dataframe, but I would like the filter to update every time I press the button. It works the first time, but apparently afterwards the observeEvent stays activated and the information is filtered even if you don't press the button. Is there any way to avoid this?
The code is the following:
library(shiny)
library(dplyr)
library(shinyWidgets)
x <- tibble(
val1 = 1:5,
val2 = sample(letters,5)
)
shinyApp(
ui = fluidPage(
column(width = 3, pickerInput("filt", "filter",
choices = x$val1,
selected = x$val1,
multiple = TRUE
),
actionButton("ready", "filter data")
),
column(width = 6, textOutput("text"))
),
server = function(input, output, session){
observeEvent(input$ready,{
output$text <- renderText({
x <- x |>
filter(val1 %in% input$filt)
print(x$val2)
})
})
}
)
I think the problem is in this part:
observeEvent(input$ready,{
output$text <- renderText({
x <- x |>
filter(val1 %in% input$filt)
print(x$val2)
})
})
Thanks a lot for the help

Use the bindEvent function in shiny
library(shiny)
library(dplyr)
library(shinyWidgets)
x <- tibble(
val1 = 1:5,
val2 = sample(letters,5)
)
shinyApp(
ui = fluidPage(
column(width = 3, pickerInput("filt", "filter",
choices = x$val1,
selected = x$val1,
multiple = TRUE
),
actionButton("ready", "filter data")
),
column(width = 6, textOutput("text"))
),
server = function(input, output, session){
output$text <- renderText({
x <- x |>
filter(val1 %in% input$filt)
print(x$val1)
}) |>
bindEvent(input$ready)
}
)

Try putting it in an eventReactive() call instead of observeEvent(). Your server function would look like this instead:
server = function(input, output, session) {
filter_data <- eventReactive(input$ready, {
x <- x %>%
filter(val1 %in% input$filt)
})
output$text <- renderText({
filter_data()$val2
})
}

Related

How to replace an observeEvent with a more comprehensive reactive function in R Shiny?

The code at the bottom of this post works as intended, using observeEvent(input$choices...) in the server section. The use of input$choices is a simplification for sake of example ease. In the fuller code this excerpt derives from, the equivalent of "choices" is molded by many different inputs (call it a "floating reactive"), and unless I misunderstand observeEvent(), it won't be feasible to use observeEvent() in the fuller code because I would have to list the myriad inputs that can alter it. So, is there a way to genericize this code where it instantly captures any change to "choices" (again, "choices" is a simplified analogy for my more complex floating reactive) and outputs it to the 2nd row of the table, including added rows?
Also in the below image, I show how "choices" is a always parachuted into the 2nd position of the dataframe in all circumstances (maybe there's a simpler way to do this too):
Code:
library(rhandsontable)
library(shiny)
mydata <- data.frame('Series 1' = c(1,1,0,1), check.names = FALSE)
rownames(mydata) <- c('Term A','Floating reactive','Term C','Term D')
ui <- fluidPage(br(),
useShinyjs(),
uiOutput("choices"),br(),
rHandsontableOutput('hottable'),br(),
fluidRow(
column(1,actionButton("addSeries", "Add",width = '70px')),
column(3,hidden(uiOutput("delSeries2")))
)
)
server <- function(input, output) {
uiTable <- reactiveVal(mydata)
observeEvent(input$hottable, {uiTable(hot_to_r(input$hottable))})
output$hottable <- renderRHandsontable({
rhandsontable(uiTable(),rowHeaderWidth = 100, useTypes = TRUE)
})
observeEvent(input$choices,{
tmpTable <- uiTable()
tmpTable[2,]<- as.numeric(input$choices)
uiTable(tmpTable)
})
output$choices <-
renderUI({
selectInput(
"choices",
label = "User selects value to reflect in row 2 of table below:",
choices = c(1,2,3)
)
})
observeEvent(input$addSeries, {
newCol <- data.frame(c(1,1,0,1))
newCol[2,] <- as.numeric(input$choices)
names(newCol) <- paste("Series", ncol(hot_to_r(input$hottable)) + 1)
uiTable(cbind(uiTable(), newCol))
})
output$delSeries2 <-
renderUI(
selectInput(
"delSeries3",
label = NULL,
choices = colnames(hot_to_r(input$hottable))
)
)
}
shinyApp(ui,server)
Not sure if I get the point here, but you might want to use observe instead of observeEvent to avoid managing the reactive dependencies (eventExpr) yourself:
library(rhandsontable)
library(shiny)
library(shinyjs)
mydata <- data.frame('Series 1' = c(1,1,0,1), check.names = FALSE)
rownames(mydata) <- c('Term A','Floating reactive','Term C','Term D')
ui <- fluidPage(br(),
useShinyjs(),
uiOutput("choices"),br(),
rHandsontableOutput('hottable'),br(),
fluidRow(
column(1,actionButton("addSeries", "Add",width = '70px')),
column(3,hidden(uiOutput("delSeries2")))
)
)
server <- function(input, output) {
uiTable <- reactiveVal(mydata)
observeEvent(input$hottable, {uiTable(hot_to_r(input$hottable))})
output$hottable <- renderRHandsontable({
rhandsontable(uiTable(),rowHeaderWidth = 100, useTypes = TRUE)
})
observe({
req(input$choices)
tmpTable <- uiTable()
tmpTable[2,] <- as.numeric(input$choices)
uiTable(tmpTable)
})
output$choices <-
renderUI({
selectInput(
"choices",
label = "User selects value to reflect in row 2 of table below:",
choices = c(1,2,3)
)
})
observeEvent(input$addSeries, {
newCol <- data.frame(c(1,1,0,1))
newCol[2,] <- as.numeric(input$choices)
names(newCol) <- paste("Series", ncol(hot_to_r(input$hottable)) + 1)
uiTable(cbind(uiTable(), newCol))
})
output$delSeries2 <-
renderUI(
selectInput(
"delSeries3",
label = NULL,
choices = colnames(hot_to_r(input$hottable))
)
)
}
shinyApp(ui,server)

Getting error using factor() function inside a reactive expression in the shiny app

I am developing a shiny app for regression analysis. I get an error when I want to change some variables to factor using the factor() function.
I want the user to select the variables he\she wants to change to factor from a selectInoput() and use a reactive function to feed the results to a new dataframe but the result is very weird! :(
I put a simplified version of what I do here.
Spent a day and could find the solution. Would appreciate your help.
x <- c( 1:5 )
y <- c( 10:14)
df <- data.frame(
x = x,
y = y
)
library(shiny)
ui <- fluidPage(
titlePanel(""),
sidebarLayout(
sidebarPanel(
selectInput(inputId = "str", label = "which variables should be changed to factors",choices = names(df), multiple = T)
),
mainPanel(
verbatimTextOutput("output")
)
)
)
server <- function(input, output) {
df_2 <- reactive({
df[ , input$str ] <- factor(df[ , input$str ])
})
output$output <- renderPrint({
str( df_2() )
})
}
shinyApp(ui = ui, server = server)
Your code only needs minor modification in server.
x <- c( 1:5 )
y <- c( 10:14)
df <- data.frame(
x = x,
y = y
)
library(shiny)
ui <- fluidPage(
titlePanel(""),
sidebarLayout(
sidebarPanel(
selectInput(inputId = "vars", label = "which variables should be changed to factors",choices = names(df), multiple = T)
),
mainPanel(
verbatimTextOutput("output")
)
)
)
server <- function(input, output) {
selected_vars <- reactive(input$vars)
df_2 <- reactive({
df[selected_vars()] <- lapply(df[selected_vars()], as.factor)
return(df)
})
output$output <- renderPrint({
str(df_2())
})
}
shinyApp(ui = ui, server = server)

Access the returned values of dynamically created Shiny modules

I am looking to build a shiny app that dynamically creates modules (via callmodule) that returns a simple form. I have 2 loose ends on it that I would appreciate some guidance on please.
Firstly, when multiple forms are brought to the user (via a button click), the values on previously rendered forms revert to the default. How do I stop this behaviour so that values stay on the users selection?
And 2, how do I access and present ‘all’ the values from the selections into a single tibble that can be shown in a tableOutput?
I have put a simple example together below using observeEvent; I also tried a variation with eventReactive however I just can’t seem to access the callmodule outputs.
Thnx in advance!
library(shiny)
library(stringr)
gen_r_8_formUI <- function(id){
ns <- NS(id)
tagList(fluidRow(column(width = 4, selectInput(ns("slt_forename"), 'forename', choices = unique(c("john", "paul", "george", "ringo")))),
column(width = 4, selectInput(ns("slt_surname") , 'surname' , choices = unique(c("lennon", "mccartney", "harrison", "starr"))))))
}
gen_r_8_form <- function(input, output, session){
select_values <- reactiveValues(forename = NULL, surname = NULL)
observeEvent(input$slt_forename,{select_values$forename <- input$slt_forename})
observeEvent(input$slt_surname, {select_values$surname <- input$slt_surname})
select_values_all <- reactive({tibble(forename = select_values$forename, surname = select_values$surname)})
return(list(select_values_all = reactive({select_values_all()})))
}
ui <- fluidPage(
column(width = 2, actionButton("btn_gen_r_8_form", "GEN R 8 a FORM")),
column(width = 6, uiOutput("all_ui_forms")),
column(width = 4, tableOutput("all_form_values_table")))
server <- function(input, output) {
rctv_uis <- reactiveValues(all_ui = list())
gen_forms <- reactiveValues(all_form_values = list())
output$all_ui_forms <- renderUI({tagList(rctv_uis$all_ui)})
output$all_form_values_table <- renderTable({all_form_values_rctv()})
observeEvent(input$btn_gen_r_8_form, {
x_id <- paste( "ns_", str_replace_all(paste(Sys.time()), "-| |:", '') , sep = '')
gen_forms$all_form_values[[x_id]] <- callModule(module = gen_r_8_form, id = x_id)
rctv_uis$all_ui[[x_id]] <- gen_r_8_formUI(id = x_id)
})
all_form_values_rctv <- reactive({
# Question - how to make a tibble with all form values?
# tibble(
# forenames = 'all gen_forms$all_form_values forenames',
# surnames = 'all gen_forms$all_form_values surnames'
# )
})
}
shinyApp(ui = ui, server = server)
Here is a solution that uses insertUI. It has the advantage that existing UI elements stay the same (no resetting of the previous modules) and only new modules are added. To determine where the UI is added, define a tags$div(id = "tag_that_determines_the_position") in the UI function. Then, insertUI takes this as an argument. Additionally, I've changed a few things:
simplified the code for the module server function, you basically only need a reactive
use of the new module interface introduced with shiny 1.5.0
use a bit simpler reactive data structure (less nesting)
library(shiny)
library(stringr)
gen_r_8_formUI <- function(id){
ns <- NS(id)
tagList(fluidRow(column(width = 4, selectInput(ns("slt_forename"), 'forename', choices = unique(c("john", "paul", "george", "ringo")))),
column(width = 4, selectInput(ns("slt_surname") , 'surname' , choices = unique(c("lennon", "mccartney", "harrison", "starr"))))))
}
gen_r_8_form <- function(id){
moduleServer(
id,
function(input, output, session) {
select_values_all <- reactive({tibble(forename = input$slt_forename,
surname = input$slt_surname)})
return(list(select_values_all = reactive({select_values_all()})))
}
)
}
ui <- fluidPage(
column(width = 2, actionButton("btn_gen_r_8_form", "GEN R 8 a FORM")),
column(width = 6, tags$div(id = "add_UI_here")),
column(width = 4, tableOutput("all_form_values_table")))
server <- function(input, output) {
gen_forms <- reactiveValues()
current_id <- 1
observeEvent(input$btn_gen_r_8_form, {
x_id <- paste0("module_", current_id)
gen_forms[[x_id]] <- gen_r_8_form(id = x_id)
insertUI(selector = "#add_UI_here",
ui = gen_r_8_formUI(x_id))
current_id <<- current_id + 1
})
all_form_values_rctv <- reactive({
res <- lapply(reactiveValuesToList(gen_forms), function(current_module_output) {
current_module_output$select_values_all()
})
# prevent to show an error message when the first module is added
if (length(res) != 0 && !is.null(res[[1]]$forename)) {
dplyr::bind_rows(res)
} else {
NULL
}
})
output$all_form_values_table <- renderTable({
all_form_values_rctv()
})
}
shinyApp(ui = ui, server = server)
I think you want something like this
all_form_values_rctv <- reactive({
dplyr::bind_rows(lapply(gen_forms$all_form_values, function(x) {
x$select_values_all()
}))
})
You've collected all the model reactive elements in gen_forms$all_form_values so you iterate over them and get the reactive value and then bind all those tables together.

R shiny Error: object 'input' not found, when used in eventReactive and Desctools

I know it might be duplicated, and I have sought for several questions that is similar with, but I still can not find why my code not work on.
The error occurs when two input sources are compiled to the eventReactive part.
My bug code like this:
library(shiny)
library(rio)
library(DescTools)
options(shiny.maxRequestSize=500*1024^2,shiny.usecairo = FALSE)
ui <- fluidPage(
titlePanel("See the file table"),
fluidRow(
column(4,
fileInput("theFile","upload your file")
),
column(4,
radioButtons("encode", "encoding way",
choices = c("Default" = "default",
"UTF-8" = "utf_8"),selected = "default")
),
column(4,
uiOutput("a_input")
),
column(4,
uiOutput("b_input")
),
column(4,
actionButton("choice3", "Show two variables comparing")
),
column(12,
verbatimTextOutput("console_comp")
),
column(12,
plotOutput("plot_Desc_comp")
)
)
)
server <- function(input,output, session){
allData <- reactive({
theFile <- input$theFile
req(input$theFile)
# Changes in read.table
if(input$encode == "default"){
df <- import(theFile$datapath)
} else{
df <- import(theFile$datapath,encoding = "UTF-8")
return(df)
}
})
output$a_input <- renderUI({
cn <- colnames(allData())
selectInput("a_input", "Select A variable to compare with Desc",
choices = cn,
size=10,
multiple=F, selectize=FALSE)
})
output$b_input <- renderUI({
cn <- colnames(allData())
selectInput("b_input", "Select B variable to compare with Desc",
choices = cn,
size=10,
multiple=F, selectize=FALSE)
})
data_Desc_a <- eventReactive(input$choice3, {
req(allData())
dat <- allData()
dat[,input$a_input, drop = FALSE]
})
data_Desc_b <- eventReactive(input$choice3, {
req(allData())
dat <- allData()
dat[,input$b_input, drop = FALSE]
})
output$console_comp <- renderPrint({
dat <- allData()
var_a <- data_Desc_a()
var_b <- data_Desc_b()
mylist2 <- Desc(var_a ~ var_b, dat)
print(mylist2)
})
output$plot_Desc_comp <- renderPlot({
dat <- allData()
var_a <- data_Desc_a()
var_b <- data_Desc_b()
mylist2 <- Desc(var_a ~ var_b, dat)
plot(mylist2)
})
}
shinyApp(ui, server)
The error code occurs when I want to press the "Show two variables comparing" buttom after I uploaded one file and chose two vars, and the error like this:
unused arguments (var_a ~ var_b, dat)
Even if I just use one source, it can work things out.
My work code like this:
ui <- fluidPage(
titlePanel("See the file table"),
fluidRow(
column(6,
fileInput("theFile","upload your file")
),
column(6,
radioButtons("encode", "encoding way",
choices = c("Default" = "default",
"UTF-8" = "utf_8"),selected = "default")
),
column(8,
uiOutput("colToDesc")
),
column(4,
actionButton("choice2", "Show variables Desc")
),
column(12,
verbatimTextOutput("console")
),
column(12,
plotOutput("plot_Desc")
)
)
)
server <- function(input,output, session){
allData <- reactive({
theFile <- input$theFile
req(input$theFile)
# Changes in read.table
if(input$encode == "default"){
df <- import(theFile$datapath)
} else{
df <- import(theFile$datapath,encoding = "UTF-8")
return(df)
}
})
output$colToDesc <- renderUI({
cn <- colnames(allData())
selectInput("colToDesc", "Select variable to Desc",
choices = cn,
size=10,
multiple=T, selectize=FALSE)
})
data_Desc <- eventReactive(input$choice2, {
req(allData())
dat <- allData()
dat[,input$colToDesc, drop = FALSE]
})
output$console <- renderPrint({
variables <- data_Desc()
mylist <- Desc(variables,main = names(variables))
print(mylist)
})
output$plot_Desc <- renderPlot({
variables <- data_Desc()
mylist <- Desc(variables,main = names(variables))
plot(mylist)
})
}
And I can sure the function of Desc from DescTools package can work well like this :
Desc(temp[,91]~temp[,5],temp)
So what's wrong with my bug code.

Reactive select input to update table

I am trying to understand the reactive part in R shiny. In that process I am trying to update an output table based on the input change while selecting values from the age drop down. It seems to do it by the first value but when I change any value from the age drop down it won't update my table. The input I am using is chooseage. Below is the code which I am using.
library(shiny)
library(shinydashboard)
library(shinyBS)
library(knitr)
library(kableExtra)
library(shiny)
library(shinythemes)
ui <- dashboardPage(
dashboardHeader(disable = F, title = "PATH Study"),
dashboardSidebar(sidebarMenu(
menuItem(
"Population Filter",
uiOutput("choose_age")
)
)),
dashboardBody(box(
width = 12,
tabBox(
width = 12,
id = "tabBox_next_previous",
tabPanel("Initiation",
fluidRow(
box(
width = 5,
solidHeader = TRUE,
status = "primary",
tableOutput("smoke"),
collapsible = F
)
))
),
uiOutput("Next_Previous")
))
)
server <- function(input, output, session) {
# Drop-down selection box for which Age bracket to be selected
age_levels <- c("18 to 24 years old","25 to 34 years old","35 to 44 years old")
output$choose_age <- renderUI({
selectInput("selected_age", "Age", as.list(age_levels))
})
myData <- reactive({
with_demo_vars %>%
filter(age == input$choose_age) %>%
pct_ever_user(type = "SM")
})
output$smoke <-
renderTable({
head(myData())
})
}
shinyApp(ui = ui, server = server)
Here is a quick prototype for your task
library(shiny)
library(tidyverse)
library(DT)
# 1. Dataset
df_demo <- data.frame(
age = c(16, 17, 18, 19, 20),
name = c("Peter", "Mary", "Mike", "Nick", "Phillipe"))
# 2. Server
server <- function(input, output, session) {
# 1. UI element 'Age'
output$ui_select_age <- renderUI({
selectInput("si_age", "Age", df_demo$age)
})
# 2. Reactive data set
df_data <- reactive({
# 1. Read UI element
age_selected <- input$si_age
# 2. Filter data
df <- df_demo %>%
filter(age == age_selected)
# 3. Return result
df
})
# 3. Datatable
output$dt_table <- renderDataTable({
datatable(df_data())
})
}
# 3. UI
ui <- fluidPage(
fluidRow(uiOutput("ui_select_age")),
fluidRow(dataTableOutput("dt_table"))
)
# 4. Run app
shinyApp(ui = ui, server = server)
I think youre shinyApp is over-reactive, as all functions in the server are executed straight away, without waiting for any selected input. So either it will break down or behave weird. So you have to delay the reactivity with req(), validate() / need() or with any observeEvent or eventReactive() function.
Maybe this snippet might help you, although there would be several ways to achieve the desired behaviour.
library(shiny)
library(shinydashboard)
library(dplyr)
data(mtcars)
mtcars$age <- sample(x = c(10,20,30,40,50), size = nrow(mtcars), replace = T)
with_demo_vars <- mtcars
ui <- dashboardPage(
dashboardHeader(disable = F, title = "PATH Study"),
dashboardSidebar(sidebarMenu(
menuItem(text = "Population Filter",
uiOutput("choose_age")
)
)
),
dashboardBody(
tableOutput("smoke")
)
)
server <- function(input, output, session) {
output$choose_age <- renderUI({
selectInput("selected_age", "Age", with_demo_vars$age)
})
myData <- reactive({
with_demo_vars %>%
dplyr::filter(age == input$selected_age)
})
output$smoke <- renderTable({
req(input$selected_age)
head(myData())
})
}
shinyApp(ui = ui, server = server)

Resources