Using help from here, I'm trying to make a shiny app that looks like:
You can add more such
selectInput
and textInput
using the Add Operation
button (and delete them using Remove Operation
button). My code goes as:
library(shiny)
mylist <- c('boxPlot','logNorm','pca')
params <- list()
params[[1]] <- list('columns')
params[[2]] <- c()
params[[3]] <- c('column','nComp')
ui <- shinyUI(fluidPage(
sidebarPanel(
actionButton("add_btn", "Add Operation"),
actionButton("rm_btn", "Remove Operation")
),
mainPanel(uiOutput("textbox_ui"))
))
server <- shinyServer(function(input, output, session){
# Track the number of input boxes to render
counter <- reactiveValues(n = 0)
#Track the number of input boxes previously
prevcount <-reactiveValues(n = 0)
observeEvent(input$add_btn, {
counter$n <- counter$n + 1
prevcount$n <- counter$n - 1
})
observeEvent(input$rm_btn, {
if (counter$n > 0) {
counter$n <- counter$n - 1
prevcount$n <- counter$n + 1
}
})
textboxes <- reactive({
n <- counter$n
vals = c()
indexOp = c()
if (n > 0) {
# If the no. of textboxes previously where more than zero, then
#save the text inputs in those text boxes
if(prevcount$n > 0){
paramsVal <- list()
if(prevcount$n > n){
lesscnt <- n
isInc <- FALSE
}
else{
lesscnt <- prevcount$n
isInc <- TRUE
}
for(i in 1:lesscnt){
inpid = paste0("textin",i)
vals[i] = input[[inpid]]
for(j in 1:length(mylist)){
# if(strcmp(vals[i],mylist[j])){
if(vals[i]==mylist[j]){
indexOp[i] = j
break
}
}
}
if(isInc){
vals <- c(vals, mylist[1])
indexOp <- c(indexOp,1)
}
lapply(seq_len(n), function(i) {
tagList(
selectInput(
inputId = paste0("textin", i),
label = h3("Select operation"),
choices = mylist,
selected = vals[i]
),
lapply(seq_len(length(params[[indexOp[i]]])), function(j) {
textInput(inputId = paste0("params",i,"_",j),label = params[[indexOp[i]]][j],value = "Parameter")
})
)
})
}
else{
indexOp[1] = 1
lapply(seq_len(n), function(i) {
tagList(
selectInput(
inputId = paste0("textin", i),
label = h3("Select operation"),
choices = mylist,
selected = mylist[1]
),
lapply(seq_len(length(params[[indexOp[i]]])), function(j) {
textInput(inputId = paste0("params",i,"_",j),label = params[[indexOp[i]]][j],value = "Parameter")
})
)
})
}
}
})
output$textbox_ui <- renderUI({ textboxes() })
})
shinyApp(ui, server)
mylist
is the list of operations the user can choose from. params[[i]]
contains the arguments that the i-th function in mylist
would take. indexOp
just helps me retrieve the arguments based on the user input. Would be more than happy to explain the code even more if needed, please let me know as a comment if that's the case.
The issue
What I expect from it is that:
I click on add operation and get new fields for select and texts (function and argument).
Default function should be boxPlot, and default textInput must be for
columns
.When I change the function from say boxPlot to pca, I should get 2 textInputs.
1 and 2 are happening as expected. But in 3, the textInput is not changed directly. It changes only after I add another operation after that. That is, if a operation is last, its textInputs don't change instantly but I have to add another operation to do so, but if it is not the last, they change instantly.
I want it work like the latter case, i.e., they change instantly irrespective of whether the operation is last or not. How can I do that? Thanks...