I have an app where a user selects an equation from a drop down list. The app pulls all the variables that get input into the equation from a table, plugs the variables into the equation, and provides the result. However, I want a user to be able to override any of the values and see how the calculation is affected. The calculation is facilitated using eval
and a list of variables, so I suspect what I'm trying to do is change the value of the variable in the environment when a user updates a table value, but I'm open to other ideas! See screenshot and reprex provided below.
I am able to get the text box where a user can input their own values for the variables. (Small victory!) I tried using a proxy table to facilitate the recalculation, but the problem is I haven't found an example where there is a proxy table and a dynamic input field in the table.
Many of the examples I found start with mod_df <- shiny::reactiveValues(x = df)
(or something similar). When I try to replicate the code, I get the following error:
You tried to do something that can only be done from inside a reactive consumer.
Reprex:
library(shiny)
library(DT)
require(stringr)
df1 = data.frame("ID" = c(1:3), "Equation" = c("<A> * <B> + <C>", "<A> + <B> + <C>", "<A> - <B> + <C>"))
df2 = data.frame("Equation" = c(1, 1, 1, 2, 2, 2, 3, 3, 3),
"Variable" = c("A", "B", "C", "A", "B", "C", "A", "B", "C"),
"Value" = c(5,3,10,2,4,NA,7,5,NA))
df3 = data.frame("Premise" = c("Red", "Yellow", "Green"),
"Value" = c(5,4,3))
ui <- fluidPage(
selectizeInput("df1Select", "Select Equation", choices = df1$Equation, multiple = TRUE, options = list(maxItems = 1)),
h3("This is the static table of the variables as defined"),
dataTableOutput("StaticTable"),
br(),
htmlOutput("CalcResultStatic"),
h3("Here, I want to be able to manipulate the fields and recalculate the results"),
dataTableOutput("InteractiveTable"),
br(),
htmlOutput("CalcResultInteractive"))
server <- function(input, output, session) {
### Filter Data
StaticTableFiltered = reactive({
EqSelect = if(is.null(input$df1Select)) unique(as.vector(df1$ID)) else df1$ID[df1$Equation == input$df1Select]
filter(df2, Equation %in% EqSelect)})
### Render Static Table
output$StaticTable = renderDataTable({
if(is.null(input$df1Select)) return(NULL) else {StaticTableFiltered()}},
options = list(dom = 't'), selection = "none")
### Render Static Result as Text
CalculateResultStatic = reactive({
if(is.null(input$df1Select)) return(NULL) else {
### Define algorithm
CalcEq = paste(input$df1Select)
### Make list of input variables
Variables = as.data.frame(str_extract_all(input$df1Select, "<(.*?)>"))
colnames(Variables)[1] = "Vars"
### Remove < > from algorithm and assumptions
CalcEq = gsub("<","",gsub(">","",CalcEq))
Variables$Vars = gsub("<","",gsub(">","",Variables$Vars))
### Get values from Assumptions table
Variables = merge(Variables, df2[df2$Equation == df1$ID[df1$Equation == input$df1Select],], by.x = "Vars", by.y = "Variable", all.x = TRUE, all.y = FALSE)
### Convert variables to list
Variables.List = as.list(Variables$Value)
names(Variables.List) = Variables$Vars
### Evaluate equation on list
eval(str2lang(CalcEq), envir = Variables.List)}})
output$CalcResultStatic = renderUI(HTML(paste("<b>",CalculateResultStatic(), sep = "")))
### Render Interactive Table
output$InteractiveTable = renderDataTable({
if(is.null(input$df1Select)) return(NULL) else {
data = data.frame(StaticTableFiltered(),
UserValue = sapply(1:nrow(StaticTableFiltered()), function(i) {sprintf('<input id="text" type="text" class="form-control" value=""/>', i)}))}},
selection = "none", escape = FALSE,
options =
list(
preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node()); }'),
drawCallback = JS('function() { Shiny.bindAll(this.api().table().node()); } '),
dom = 't',
processing = FALSE))
### Render Interactive Result as Text
CalculateResultInteractive = reactive({
if(is.null(input$df1Select)) return(NULL) else {
### Define algorithm
CalcEq = paste(input$df1Select)
### Make list of input variables
Variables = as.data.frame(str_extract_all(input$df1Select, "<(.*?)>"))
colnames(Variables)[1] = "Vars"
### Remove < > from algorithm and assumptions
CalcEq = gsub("<","",gsub(">","",CalcEq))
Variables$Vars = gsub("<","",gsub(">","",Variables$Vars))
### Get values from Assumptions table
Variables = merge(Variables, df2[df2$Equation == df1$ID[df1$Equation == input$df1Select],], by.x = "Vars", by.y = "Variable", all.x = TRUE, all.y = FALSE)
### Convert variables to list
Variables.List = as.list(Variables$Value)
names(Variables.List) = Variables$Vars
### Evaluate equation on list
eval(str2lang(CalcEq), envir = Variables.List)}})
output$CalcResultInteractive = renderUI(HTML(paste("<b>",CalculateResultInteractive(), sep = "")))
}
shinyApp(ui = ui, server = server)
In addition, if you play with the reprex, you'll see some equations require a value to be looked up. I also wanted to create a column to facilitate the lookup, but I thought let's start with just the text field and see if the help I get there guides me in the right direction to do the next piece myself.
Thanks in advance for any help!