R Shiny ObserveEvent with multiple inputs - r

The below code is working, but I need to enhance it by observing multiple inputs.
I need a multiple observeEvent on input$dateinput which selects the xlsx file to open and input$myfilter which checks if the user wants to apply a specific filter to the data.
but when I change
observeEvent(input$dateinput,... to:
observeEvent( c(input$dateinput, input$myfilter),{
The app crashes with Warning: Error in file: invalid 'description' argument [No stack trace available]
The code otherwise runs fine. Any help? thanks
full code : EDIT: THIS IS NOW REPRODUCIBLE AND DOES NOT REQUIRE ANY EXCEL FILE
library(shiny)
library(shinyWidgets)
library(openxlsx)
opendir <- function(dir = getwd()){
if (.Platform['OS.type'] == "windows"){
shell.exec(dir)
} else {
system(paste(Sys.getenv("R_BROWSER"), dir))
}
}
ui <- fluidPage(
sidebarPanel(
uiOutput("gpui")
),
mainPanel(
titlePanel("test app"),
br(),
checkboxInput("myfilter", label = "Filter all unnecessary (71, 46, 44) documents", value = TRUE),
br(),
tableOutput("datatable")
)
)
server <- function(input, output, session) {
rvalues <- reactiveValues()
rvalues$listfiles <- list.files(pattern=".xlsx")
observeEvent(input$refresh, {
print(input$dateinput)
session$reload()
})
observeEvent(input$openfolder, {
opendir()
})
output$gpui <- renderUI({
tagList(
actionButton("openfolder", "Open Data Folder"),
actionButton("refresh", "Refresh data folder"),
pickerInput("dateinput","Choose the date", choices=isolate(rvalues$listfiles), options = list(`actions-box` = TRUE),multiple = F)
)
})
observeEvent(input$myfilter,{
print("myfilter")
})
observeEvent( input$dateinput ,{
print(input$dateinput)
print("selecteddata")
cols <- c("Purchasing.Document", "Net.Order.Value", "Currency", "G/L.Account",
"Short.Text",
"Requisitioner", "Release.indicator", "Deletion.indicator")
seldata <- read.xlsx(input$dateinput)
print(nrow(seldata))
seldata <- seldata[,cols]
myfilter <- substr(seldata$Purchasing.Document,1,2) %in% c("71", "46", "44")
if(input$myfilter) {
rvalues$data <- seldata[myfilter,]
}
rvalues$data <- seldata
})
output$datatable <- renderTable(
rvalues$data,
striped = T,
spacing = 's'
)
}
shinyApp(ui, server)

For multiple observes in observeEvent() wrap them in curly brackets without commas, just as regular code.
Try:
shiny::observeEvent(
eventExpr = {
input$dataInput
input$myFilter
},
handlerExpr = {
# You code to run
}
)
In my experience it can be safer to wrap complex observeEvent expressions (handlerExpr) in isolate({}) to suppress any undesired reactivity.

When your observer reacts to input$myfilter, it is triggered at the startup. And input$dateinput is NULL. So you get this error:
> openxlsx::read.xlsx(NULL)
Error in file(description = xlsxFile) : argument 'description' incorrect

Related

Saving user defined variables and running R scipt in Shiny

I have a shiny app that saves a few variables globally. I would like for the user to be able to click a button 'Run' That would 1) save the variables globally and 2) run an R script that uses those variables.
Below is where I am at, but I am not able to save the variables before hitting the button.
library(shiny)
ui <- fluidPage(
column(4, wellPanel(dateInput('date', label = 'Date input: yyyy-mm-dd', value = Sys.Date()))),
column(4, wellPanel(numericInput('STD', 'STD', 1.2))),
actionButton("Run", "Run the tool")
)
server <- function(input, output) {
observeEvent(input$STD, {
STDShiny <<- input$STD1
})
observeEvent(input$date, {
dateShiny <<- input$date
})
observeEvent(input$Run, {
source("someScript.R")
})
}
Example script: someScript.R
dir.create(paste(date,STD, sep = ''))
Any assistance is appreciated.
Somescript.R code:
dir.create(paste(.GlobalEnv$dateShiny, .GlobalEnv$STDShiny, sep = ''))
Shinyapp:
library(shiny)
library(tidyverse)
ui <- fluidPage(
column(4, wellPanel(dateInput('date', label = 'Date input: yyyy-mm-dd', value = Sys.Date()))),
column(4, wellPanel(numericInput('STD', 'STD', 1.2))),
actionButton("Run", "Run the tool") #The button to trigger script
)
server <- function(input, output) {
#Upon clicking in the button the following code gets executed
observeEvent(input$Run,{
#declare as variables in the global env with the values of the inputs
walk2(c('STDShiny', 'dateShiny'), c(input$STD, input$date), ~{
assign(..1, ..2, envir = .GlobalEnv)
})
#Run the script
exec(source, file = 'someScript.R')
})}
shinyApp(ui, server)

How to fix editable DT::datatable throwing: "Error in split.default: first argument must be a vector"

I am trying to make a module that accepts a data frame and produces an editable datatable out of it. This worked until I made the module able to accept multiple edits by making the following change:
editTable <- reactive({
datatable(
reactives$input,
#editable = T #PREVIOUS (working fine)
editable = list(target = "all"), #NEW (problem-causing)
rownames = F
)
})
Once the code labelled #NEW is included, clicking labelDo (in this case "Edit") causes the app to crash with this error message:
Warning: Error in split.default: first argument must be a vector
The closest problem I could find to this one is here . This user's problem is the same but mine is not solved (as theirs allegedly is) by putting rownames = FALSE into their datatable() equivalent of the snippet above.
Please go ahead and run the following module and app together and attempt to edit one of the numbers in the table. Click 'edit' and you will get the same result.
Module:
editrUI <- function(id, labelDo, labelUndo) {
ns <- NS(id)
tagList(
dataTableOutput(ns("out")),
actionButton(
inputId = ns("do"),
label = labelDo
),
actionButton(
inputId = ns("undo"),
label = labelUndo
)
)
}
editrServer <- function(id, dataFrame) {
moduleServer(
id,
function(input, output, session){
reactives <- reactiveValues()
reactives$input <- NULL
observe({
reactives$input <- dataFrame
})
editTable <- reactive({
datatable(
reactives$input,
#editable = T #old
editable = list(target = "all"), #new
rownames = F
)
})
output$out <- renderDataTable(
editTable()
)
observeEvent(input$do , {
reactives$input <<- editData(reactives$input, input$out_cell_edit, rownames = F)
})
observeEvent(input$undo , {
reactives$input <- dataFrame
})
return(reactive({reactives$input}))
}
)
}
App:
library(shiny)
source(
#source of module
)
a <- 1:5
df <- tibble(a, a*2)
ui <- fluidPage(
editrUI(id = "id", labelDo = "Edit", labelUndo = "Undo")
)
server <- function(input, output) {
editrServer(id = "id", dataFrame = df)
}
# Run the application
shinyApp(ui = ui, server = server)
It seems this error is caused when input$out_cell_edit is NULL (no cell has been edited).
You can fix it with req(input$out_cell_edit) that will cancel the event in case input$out_cell_edit is NULL.
Also you don't need to use <<- to assign to the reactiveValues.
observeEvent(input$do , {
req(input$out_cell_edit)
reactives$input <- editData(reactives$input, input$out_cell_edit, rownames = F)
})

Data available in Global Environment but value not accessible

Hi fellow Shiny users,
I am been struggling with this bug for a day and would really appreciate your help.
Goal: To use updateSelectInput to refresh the variable selection in the drop down list.
Problem: The drop down list is empty, even though "exists("data", envir = .GlobalEnv)" indicates that "data" exists.
Below is my code. Note that "data" is a global dataset created in myFunctions.R.
rawdata:
colA <- c('1','2','3','3','2')
colB <- c('1','1','3','3','2')
colC <- c('14','12','33','33','26')
rawdata <- as.data.frame(cbind(colA,colB, colC))
ui.R:
fluidPage(
navbarPage(strong("My Structure"), id = "allResults",
tabPanel(value ='inputData', title = 'Run Structure',
sidebarLayout(
sidebarPanel(
actionButton("runButton", "Run Structure!"),
br(),
br(),
selectInput("selectvar", label = ("Select a variable"), choices = "")
),
mainPanel(
DT::dataTableOutput('structure')
)
)
),
tabPanel(value='temp',title="TEMP", verbatimTextOutput("temp"))
)
)
server.R:
source("./myFunctions.R")
library("DT")
function(input, output, session) {
# Run functions
structure_result <- eventReactive (input$runButton, {
assign('data', rawdata, envir=.GlobalEnv)
PrepareData(rawdata)
as.data.frame(head(data))
})
# Make a reactive copy of 'data' (a global dataset created in myFunctions.R)
data_copy <- reactiveValues()
observe({
if(exists("data") && is.data.frame(get("data", envir =.GlobalEnv))) {
data_copy$df <- get("data", envir = .GlobalEnv)
}
})
# Update SelectInput drop down list
observe({
req(data_copy$df)
updateSelectInput(session, "selectvar", choices = names(data_copy$df [ , 1:ncol(data_copy$df)]))
})
# Selected variable
var_dd <- reactiveValues()
observeEvent(input$selectvar, {
req(data_copy$df)
var_dd$selected <- match(input$selectvar, names(data_copy$df [ , 1:ncol(data_copy$df)]))
})
# Display structure results
output$structure <- DT::renderDataTable({
DT::datatable(structure_result(), options = list(paging = FALSE, searching = FALSE))
})
}
myFunctions.R:
PrepareData<-function(data) {
data<<-RankData(data)
}
RankData<-function(datax) {
return(datax[order(datax[,1],datax[,2]),])
}
Any help would be greatly appreciated. Thank you!
You have to source your functions inside (rather than before) the server function and use the local=TRUE option.
library("DT")
function(input, output, session) {
source("./myFunctions.R", local=TRUE)
# ...
}
Here you find more information on shiny's scoping rules: Link

argument of length is zero. Program does not recognize option from SelectInput

SelectInput function in ui is supposed to give me an option to choose "YES" or "NO". When "NO" is selected, it will choose the " if(("NO" %in% input$qualify_pit))" block in renderDataTable function in server and execute that perfectly. However, when I choose "YES" option, its block does not run, not displaying any table. I tried everything to get it to run its block (if(("YES" %in% input$qualify_pit))) but to no avail.
library(shiny)
library(shinythemes)
library(DT)
pit <- read.csv("PIT_STAT.csv")
pit_stat <- c("MLB_name","MLBId","LastName","FirstName","LW","W","L","Sv","BS","HLD","G","GS","CG","GF","QS","SHO","IP","BFP","H","X1B","X2B","X3B",
"HR","R","ER","SH","SF","HBP","BB","IBB","BB_noIBB","K","WP","BLK","GB","FB","LD","POPU","SB",
"CS","PKO","SVO","OUTS","K9","BB9","AVG","BABIP","HR9","GB_percent","HRperFB","ERA","KperBB",
"K_percent","BB_percent","K_minus_BB","WHIP","LD_percent","FB_percent","GBperFB")
pit_def <- c("MLB_name","MLBId","LastName","FirstName","LW","W","L","ERA","IP","H","HR","R","ER","BB","K","K9","BB9","HR9","WHIP",
"GB_percent","FB_percent","LD_percent","K_percent","BB_percent","KperBB","K_minus_BB")
ui <- shinyUI(fluidPage(
shinythemes::themeSelector(),
theme = shinytheme("paper"),
titlePanel("WSFB Stats Lab"),
fluidRow(
uiOutput("uis")
),
fluidRow(
tabsetPanel(id = "tabs",
tabPanel("Pitch Table",dataTableOutput("pitch_table"))
)
)
)
)
server <- shinyServer(function(input, output, session){
output$uis <- renderUI({
if(input$tabs == "Pitch Table")
{
pit <- read.csv("PIT_STAT.csv")
pit_stat <- c("MLB_name","MLBId","LastName","FirstName","LW","W","L","Sv","BS","HLD","G","GS","CG","GF","QS","SHO","IP","BFP","H","X1B","X2B","X3B",
"HR","R","ER","SH","SF","HBP","BB","IBB","BB_noIBB","K","WP","BLK","GB","FB","LD","POPU","SB",
"CS","PKO","SVO","OUTS","K9","BB9","AVG","BABIP","HR9","GB_percent","HRperFB","ERA","KperBB",
"K_percent","BB_percent","K_minus_BB","WHIP","LD_percent","FB_percent","GBperFB")
pit_def <- c("MLB_name","MLBId","LastName","FirstName","LW","W","L","ERA","IP","H","HR","R","ER","BB","K","K9","BB9","HR9","WHIP",
"GB_percent","FB_percent","LD_percent","K_percent","BB_percent","KperBB","K_minus_BB")
wellPanel(
checkboxGroupInput('show_vars', 'Variables to display', pit_stat, inline = TRUE, selected = pit_def),
selectInput("qualify_pit","MIN IP:",choices = c("YES","NO"))
)
}
})
output$pitch_table <- renderDataTable({
if(("YES" %in% input$qualify_pit))
{
pit <- read.csv("PIT_STAT.csv")
pit2 <- pit[pit$IP >= 162,]
DT::datatable(pit2[,input$show_vars, drop = FALSE])
}
if(("NO" %in% input$qualify_pit))
{
pit <- read.csv("PIT_STAT.csv")
DT::datatable(pit[,input$show_vars, drop = FALSE])
}
})
})
shinyApp(ui = ui, server = server)
It works with NO because the DT:datatable is the last expression of the function, therefore it is the implicit return value.
But for the YES, which is not the last evaluation (the if("NO" %in%... is), you have to explicitly use return:
return(DT::datatable(pit2[,input$show_vars, drop = FALSE]))
Otherwise you can simply use else
output$pitch_table <- renderDataTable({
if(("YES" %in% input$qualify_pit))
{
DT::datatable(pit[pit$IP >= 162,input$show_vars, drop = FALSE])
}
else
{
DT::datatable(pit[,input$show_vars, drop = FALSE])
}
})
For more details about using return you can read this thread

show warning to user in shiny in R

How to show warning to user in shiny in R. The user's input is correct, but the output is not suitable to show. The aim is to remind the user only a subset data are shown due to too many. warning() is shown in console only. Thank you.
Here is a fake code to explain the question due to the original is long. There is a warning in the renderTable. it aims to check data if the data is big, only first several items will be shown.
ui.R
shinyUI(fluidPage(
titlePanel("Validation App"),
sidebarLayout(
sidebarPanel(
selectInput("data", label = "Data set",
choices = c("", "mtcars", "faithful", "iris"))
),
# Show a plot of the generated distribution
mainPanel(
tableOutput("table"),
plotOutput("plot")
)
)
))
server.R
shinyServer(function(input, output) {
data <- reactive({
validate(
need(input$data != "", "Please select a data set")
)
get(input$data, 'package:datasets')
})
output$plot <- renderPlot({
hist(data()[, 1], col = 'forestgreen', border = 'white')
})
output$table <- renderTable({
warning("Warning message.")
head(data())
})
})
Update:
I put some more work into this and made the warning panel conditional.
However it only works if I include out the textOutput("warnstat") on every page. I assume because it is not setting the javascript variable output.warnstat unless I do this.
You could just build a warning panel into your UI, and set it accordingly. Here is a simple example, but it could be more elaborate than just a verabtim print statement.
ui.r
shinyUI(fluidPage(
titlePanel("Validation App"),
sidebarLayout(
sidebarPanel(
selectInput("data", label = "Data set",
choices = c("", "mtcars", "faithful", "iris"))
),
# Show a plot of the generated distribution
mainPanel(
conditionalPanel(condition = "output.warnstat == 'Error'",
verbatimTextOutput("warnmsg")),
tableOutput("table"),
plotOutput("plot")
)
)
))
server.r
shinyServer(function(input, output) {
errstat <- reactive({
ifelse (input$data=="mtcars",T,F)
})
data <- reactive({
validate(
need(input$data != "", "Please select a data set")
)
get(input$data, 'package:datasets')
})
output$plot <- renderPlot({
hist(data()[, 1], col = 'forestgreen', border = 'white')
})
output$table <- renderTable({
warning("Warning message.")
head(data())
})
output$warnmsg <- renderPrint({
if (errstat()){
print("Warning message - blah blah blah")
print(input$data)
head(data())
} else {
print("No error")
}
})
output$warnstat <- renderText({ifelse(errstat(),"Error","No error") })
outputOptions(output, "warnstat", suspendWhenHidden=FALSE)
})
With conditional warning panel:
Without conditional warning panel:
I use this wrapping function to capture errors, warnings and messages and display them as dismissible notifications to the user.
quietly <- function(.f) {
fun <- .f %>% purrr::quietly() %>% purrr::safely()
function(...) {
res <- fun(...)
if(!is.null(res$error)) { # safely output
showNotification(res$error$message, duration = 10, type="error")
return(res$result)
}
res <- res$result # quietly output
if(!is.null(res$warnings) && length(res$warnings) > 0) {
lapply(unique(res$warnings), showNotification, duration = 10, type="warning")
}
return(res$result)
}
}

Resources