I'm creating a shiny app where my users can recode variables (kind of) manually. Currently, there are two major issues:
DT::datatable does not like when I pass the intended output to get rendered after the user has pressed 'Execute Recode'. A reactive value v$data stores an output table to be passed between tabPanels. As this is passed to DT::datatable(), v$data causes the error
'data' must be 2-dimensional (e.g. data frame or matrix)
.I'm fairly sure something may be going wrong when I'm parsing the text inputs to the recoding: i.e. paste0(paste0('input$','recode_call_when',i))
SERVER
shinyServer(function(input, output, session){
v <- reactiveValues(data=NULL)
d <- reactiveValues(print_execute_complete=FALSE)
myData <-reactive({
if(is.null(input$file1)) return(mtcars)
as.data.frame(data.table::rbindlist(lapply(X=input$file1$datapath, FUN=read.csv,
quote=input$quote, sep=input$sep, header=input$header, dec=input$decimal),
use.names = TRUE,fill=TRUE
))
})
output$contents <-
DT::renderDataTable({
return(DT::datatable(myData(), filter='top'))
})
#Count the number of recoding terms to render
counter <- reactiveValues(n = 1)
#Recoding button functionality
observeEvent(input$add_recode, {counter$n <- counter$n + 1})
observeEvent(input$rm_recode, {
if(counter$n > 0) counter$n <- counter$n - 1
})
recoding_i <- reactive({
n <- counter$n
if(n>0){
isolate({
lapply(seq_len(n),function(i){
fluidRow(
column(width=4,
textInput(inputId = paste0('recode_call_variable',i),
label=paste0('Variable_',i))),
column(width=4,
textInput(inputId = paste0('recode_call_when',i),
label=paste0('When_', i))),
column(width=4,
textInput(inputId= paste0('recode_call_then',i),
label=paste0('Then_', i)))
)
}
)
})
}
})
output$recoding <- renderUI({ recoding_i() })
#Observes press of recode button.
observeEvent(input$'execute_recode',{
d$print_execute_complete <- TRUE
})
#Observes press of recode button.
observeEvent(input$'reset_recode',{
d$print_execute_complete <- FALSE
})
#Loop over recoding input boxes.
v$data <- reactive({
if(d$print_execute_complete == TRUE){
if(is.null(v$data)){
lapply(seq_len(n), function(i){
myData() %>% mutate(paste0(paste0('input$','recode_call_variable',i), '=', 'case_when(',paste0('input$','recode_call_when',i), '~', paste0('input$','recode_call_then',i),")"))
})
} else {
lapply(seq_len(n), function(i){
v$data %>% mutate(paste0(paste0('input$','recode_call_variable',i), '=', 'case_when(',paste0('input$','recode_call_when',i), '~', paste0('input$','recode_call_then',i),")"))
})
}
}
})
#Confirmation text
output$execute_complete <- renderText({
req(d$print_execute_complete)
if(d$print_execute_complete == TRUE){
"Recoding Complete."
}
})
#Render recoded data table
output$recoded_dt <- DT::renderDataTable({
req(d$print_execute_complete == TRUE)
if(!is.null(v$data)){
return(DT::datatable(v$data, filter='top'))
} else {
return(DT::datatable(myData(),filter='top'))
}
})
})
UI
shinyUI(fluidPage(
titlePanel("Something's Wrong"),
# Input: Select a file ----
navlistPanel(
tabPanel("Import",
fileInput("file1", "Choose CSV File",
multiple = TRUE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv")),
# Horizontal line ----
tags$hr(),
# Input: Checkbox if file has header ----
checkboxInput("header", "Header", TRUE),
# Input: Select separator ----
radioButtons("sep", "Separator",
choices = c(Comma = ",",
Semicolon = ";",
Tab = "\t"),
selected = "\t"),
# Input: Select quotes ----
radioButtons("quote", "Quote",
choices = c(None = "",
"Double Quote" = '"',
"Single Quote" = "'"),
selected = '"'),
# Input: Select decimal ----
radioButtons("decimal","Decimal",
choices = c(Comma = ",",
Dot = "."),
selected=","),
# Horizontal line ----
tags$hr(),
# Main panel for displaying outputs ----
# Output: Data file ----
DT::dataTableOutput("contents")
),
tabPanel("Recoding",
h3("Instruction"),
fluidRow(p("Write a functional call in one of the action boxes below. A call takes the form of one of the following :"
,style="font-family: 'times'; font-si16pt"),
span(em("Old Variable == Value"),strong(" e.g. gear == 4")),
br(),
span(em("Old Variable > Value"),strong("e.g. gear > 4")),
br(),
span(em("Old Variable >= Value"), strong("e.g. gear >= 4")),
br(),
span(em("Old Variable != Value"),strong("e.g. gear != 4, 'is not equal to'")),
br(),
br(),
p("A variable can be inside a span:"),
br(),
span(em("Old Variable > Value & Old Variable < Value2"), strong("e.g. gear > 2 & gear <=4")),
br(),
br(),
p("A variable can be defined if it is one or the other:"),
br(),
span(em("Old Variable < Value | Old Variable == Value2"),strong("e.g. gear <= 2 | gear == 4")),
br(),
br()
),
fluidRow(actionButton('add_recode', 'Add recode term'),
actionButton('rm_recode', 'Remove recode term')),
br(),
br(),
uiOutput('recoding'),
br(),
br(),
fluidRow(actionButton('execute_recode', "Recode",icon = icon('angle-double-right')),
actionButton('reset_recode', "Reset", icon=icon('angle-double-right'))),
textOutput('execute_complete'),
br(),
br(),
br(),
DT::dataTableOutput('recoded_dt')
)
)
)
)