I have 10 actions button in my shiny app. I'm trying to create a loop but i'm having issues with the reactive values. How could I solve it ?
Here's my code for button 1:
server <- function(input, output, session)
{
value1 <- reactiveVal(0)
observeEvent(input$minus1, {newValue1 <- value1() - 1)
value1(newValue1)})
observeEvent(input$plus1, {newValue1 <- (value1() + 1)
value1(newValue1)})
output$value1 <- renderUI(actionBttn("result1", label = value1()))
}
I tried this, but it doesn't seem to work :
server <- function(input, output, session)
{
for (i in 1:10)
{
paste0("value", i) <- reactiveVal(0)
observeEvent(input$paste0("minus",i), {paste0("newValue", i) <- (paste0("value", i,"()") - 1)
paste0("value", i)(paste0("newValue", i))})
observeEvent(input$paste0("plus",i), {newValue1 <- (paste0("value", i,"()") + 1)
paste0("value", i)(paste0("newValue", i))})
output$paste0("value", i) <- renderUI(actionBttn(paste0("result", i), label = paste0("value", i,"()")))
}
}
I had worked on a similar demo in the past based on:
https://community.rstudio.com/t/one-observer-to-handle-any-number-of-buttons-in-shiny/6569/2
This makes 10 plus and 10 minus buttons, and each button will increase or decrease reactiveValues. I hope this may be helpful.
library(shiny)
library(dplyr)
ui <- fluidPage(
fluidRow(uiOutput("show_table"))
)
server <- function(input, output, session) {
Data <- reactiveValues(
Info = rep(0, 10)
)
observe({
input_btn_p <- paste0("btn_p_", 1:10)
lapply(input_btn_p, function(x){
observeEvent(input[[x]], {
i <- as.numeric(sub("btn_p_", "", x))
Data$Info[i] <- Data$Info[i] + 1
})
})
input_btn_m <- paste0("btn_m_", 1:10)
lapply(input_btn_m, function(x){
observeEvent(input[[x]], {
i <- as.numeric(sub("btn_m_", "", x))
Data$Info[i] <- Data$Info[i] - 1
})
})
})
display_table <- reactive({
data.frame(value = Data$Info) %>%
mutate(button1 = vapply(row_number(),
function(i){
actionButton(inputId = paste0("btn_p_", i), label = "Plus") %>%
as.character()
},
character(1)),
button2 = vapply(row_number(),
function(i){
actionButton(inputId = paste0("btn_m_", i), label = "Minus") %>%
as.character()
},
character(1)))
})
output$show_table <- renderUI({
display_table() %>%
select(value, button1, button2) %>%
knitr::kable(format = "html", escape = FALSE) %>%
HTML()
})
}
shinyApp(ui, server)
Related
Does Shiny can detect only common R’s objects? If yes, What objects can it observe?
For example, I tried many options with no success to detect a data.tree changes in shiny.
Does anyone know why this happens?
library(shiny)
library(data.tree)
data(acme)
ui <- fluidPage(
actionButton("go", "go" ),
tags$h2("text"),
verbatimTextOutput("text"),
tags$h2("text0"),
verbatimTextOutput("text0"),
tags$h2("text1"),
verbatimTextOutput("text1"),
tags$h2("text2"),
verbatimTextOutput("text2"),
tags$h2("text3"),
verbatimTextOutput("text3"),
tags$h2("text4"),
verbatimTextOutput("text4")
)
server <- function(input, output, session) {
anum <- reactiveValues(a = 0)
a <- reactiveValues(acme = acme, f = NULL)
b <- reactiveVal(acme)
cc <- reactive(a$acme)
observeEvent(input$go, {
z = sample(x = 1:100 , size = 1)
a$cach <<- a$acme$clone()
anum$a <<- anum$a + 1
a$acme$AddChild(paste0("New", z))
a$f <<- a$acme
b(a$acme)
print("a$acme")
print(a$acme)
print("b()")
print(b())
})
### not working
output$text = renderPrint( print(a$f) )
output$text0 = renderPrint(print(b()))
output$text1 = renderPrint(print(cc()))
### working
observe({
print(identical(a$acme, a$cach))
output$text2 = renderPrint(print(b()))
})
### working
observe({
anum$a
output$text3 = renderPrint(print(a$acme))
})
### working
observeEvent(eventExpr = anum$a, handlerExpr = {
output$text4 = renderPrint(print(a$acme))
})
}
shinyApp(ui, server)
Turns out that adding:
a$f <- 0 #to force reaction
a$f <- a$acme
a$acme <- 0 #to force reaction
a$acme <- a$f
'fixed' the problem.
library(shiny)
library(data.tree)
data(acme)
ui <- fluidPage(
actionButton("go", "go" ),
verbatimTextOutput("text"),
verbatimTextOutput("text1"),
verbatimTextOutput("text2"),
verbatimTextOutput("text3"),
verbatimTextOutput("text4")
)
server <- function(input, output, session) {
anum <- reactiveValues(a = 0)
a <- reactiveValues(acme = acme, f = NULL)
b <- reactiveVal(acme)
cc <- reactive(a$acme)
observeEvent(input$go, {
z <- sample(x = 1:100 , size = 1)
a$cach <- a$acme$clone()
anum$a <- anum$a + 1
a$acme$AddChild(paste0("New", z))
a$f <- 0 #to force reaction
a$f <- a$acme
a$acme <- 0 #to force reaction
a$acme <- a$f
b(a$acme)
print("a$acme")
print(a$acme)
print("b()")
print(b())
})
### not working
output$text = renderPrint({
req(a$f)
print(a$f)})
output$text2 = renderPrint(print(cc()))
## now it works
observe({
print(identical(a$acme, a$cach)) #this is triggering the update
output$text1 = renderPrint(print(b()))
})
### working
observe({
anum$a
output$text3 = renderPrint(print(a$acme))
})
### working
observeEvent(eventExpr = anum$a, handlerExpr = {
output$text4 = renderPrint(print(a$acme))
})
}
shinyApp(ui, server)
I think there's something going on with a$acme$AddChild(paste0("New", z)) method that is not detected as a change when called.
I am trying to make an app which can dynamically create multiple tabs with tables in the 1st tab (Input tab), and use the dynamically created tables to do calculations in the 2nd tab (Results tab). However, I am not sure how to use the hot_to_r function to access the values from the tables created in the Input tab to do calculations in the Results tab. As an example, I want to take column i+1 divide by column i in the tables in the Input tab and display the results in the Results tab.
Below are the sample codes:
library(shiny)
library(rhandsontable)
ui <- navbarPage("App",
tabPanel("Input",
numericInput('num_of_table', "Number of sub tabs: ", value = 1, min = 1, max = 10),
uiOutput("input")),
tabPanel("Results",
uiOutput("results"))
)
server <- function(input, output,session) {
### Input ###
input_table<- reactive({
list_of_input_table = list()
for (i in c(1:input$num_of_table)){
mat <- matrix(c(1:25) * i, ncol = 5, nrow = 5)
list_of_input_table[[i]] = mat
}
index = c(1:i)
list_of_input_table[index]
})
observeEvent(input$num_of_table, {
lapply(seq_len(input$num_of_table), function(i) {
output[[paste0('input_table_', i)]] <- renderRHandsontable({
rhandsontable(input_table()[[i]])
})
})
})
output$input <- renderUI({
nTabs = input$num_of_table
myTabs1 = lapply(seq_len(nTabs), function(x){
tabPanel(paste("Tab", x),
column(12,
rHandsontableOutput(paste0("input_table_", x))))
})
do.call(tabsetPanel, myTabs1)
})
### Results ###
results_table<- reactive({
list_of_results_table = list()
list_of_input_table = list()
for (i in c(1:input$num_of_table)){
for (j in c(1:5)) {
list_of_input_table[[i]] <- as.matrix(hot_to_r(input[[paste0("input_table_",i)]]))
list_of_results_table[[i]] <- matrix(as.numeric(NA), ncol = 4, nrow = 5)
list_of_results_table[[i]][,j] <- list_of_input_table[[i]][,j+1][!is.null(list_of_input_table[[i]][,j+1])] / list_of_input_table[[i]][,j]
}}
index = c(1:i)
list_of_results_table[index]
})
observeEvent(input$num_of_table, {
lapply(seq_len(input$num_of_table), function(i) {
output[[paste0('results_table_', i)]] <- renderRHandsontable({
rhandsontable(results_table()[[i]])
})
})
})
output$results <- renderUI({
nTabs = input$num_of_table
myTabs2 = lapply(seq_len(nTabs), function(x){
tabPanel(paste("Tab", x),
column(12,
rHandsontableOutput(paste0("results_table_", x))))
})
do.call(tabsetPanel, myTabs2)
})
}
shinyApp(ui,server)
Please help!!
It seems hot_to_r isn't handling matrix objects correctly in this case.
Please check the following using a data.frame instead:
library(shiny)
library(rhandsontable)
ui <- navbarPage("App",
tabPanel("Input",
numericInput('num_of_table', "Number of sub tabs: ", value = 1, min = 1, max = 10),
uiOutput("input")),
tabPanel("Results",
uiOutput("results"))
)
server <- function(input, output,session) {
### Input ###
input_table <- reactive({
list_of_input_table = list()
for (i in c(1:input$num_of_table)){
mat <- matrix(c(1:25) * i, ncol = 5, nrow = 5)
list_of_input_table[[i]] = as.data.frame(mat)
}
index = c(1:i)
list_of_input_table[index]
})
observeEvent(input$num_of_table, {
lapply(seq_len(input$num_of_table), function(i) {
output[[paste0('input_table_', i)]] <- renderRHandsontable({
rhandsontable(input_table()[[i]])
})
})
})
output$input <- renderUI({
nTabs = input$num_of_table
myTabs1 = lapply(seq_len(nTabs), function(x){
tabPanel(paste("Tab", x),
column(12,
rHandsontableOutput(paste0("input_table_", x))))
})
do.call(tabsetPanel, myTabs1)
})
### Results ###
results_table <- reactive({
list_of_results_table = list()
for (i in c(1:input$num_of_table)){
req(input[[paste0("input_table_", i)]])
list_of_results_table[[i]] <- hot_to_r(input[[paste0("input_table_", i)]])[2:5]/hot_to_r(input[[paste0("input_table_", i)]])[1:4]
}
return(list_of_results_table)
})
observeEvent(input$num_of_table, {
lapply(seq_len(input$num_of_table), function(i) {
output[[paste0('results_table_', i)]] <- renderRHandsontable({
rhandsontable(results_table()[[i]])
})
})
})
output$results <- renderUI({
nTabs = input$num_of_table
myTabs2 = lapply(seq_len(nTabs), function(x){
tabPanel(paste("Tab", x),
column(12,
rHandsontableOutput(paste0("results_table_", x))))
})
do.call(tabsetPanel, myTabs2)
})
}
shinyApp(ui,server)
I filed an issue here.
I'm trying to update a table by changing the class of each variable (string or character). I have tried several methods but most of them just don't render something. With the method used in the code bellow, I can just change one time the type of the variable and then, the table is not reactive anymore.
Do you have an idea of what should I do?
library(DT)
library(shiny)
library(tidyverse)
server = shinyServer(function(input, output, session) {
current_stage <- reactiveValues(data=NULL)
mydata <- reactive({
df <- mtcars %>% rownames_to_column("model")
current_stage$data <- df
df
})
updateData = reactive({
df = mydata()
map(1:ncol(df), function(i){
if(length(input[[paste0("col", i)]])>0){
if (input[[paste0("col", i)]]=="num"){
df[,i] <<- unlist(df[,i]) %>% as.numeric
} else if (input[[paste0("col", i)]]=="cat"){
df[,i] <<- unlist(df[,i]) %>% as.character
}
}
})
#current_stage$data <- df
df
})
output$tableau <- DT::renderDataTable({
df <- current_stage$data
class <- map_df(df, typeof)
class <- gsub("double", "numeric", class)
class <- gsub("integer", "numeric", class)
tableSelectInput <- map(1:ncol(df),
function(i) {
if (class[i] =="numeric"){
opt1 <- "num"
opt2 <- "cat"
}else{
opt1 <- "cat"
opt2 <- "num"
}
selectInput(
inputId = paste0("col", i),
label = NULL, selected = opt1,
choices = c(opt1, opt2))
}
)
# I didn't find a more elegant way to turn '[[ ]]' in '[ ]'
l <- length(tableSelectInput)
selectin <- 1:l
type_cat <- 1:l
for (i in 1:l) {
selectin[i] = as.character(tableSelectInput[[i]])
pos=gregexpr("selected>",selectin[i])[[1]][1]
type_cat[i] = substr(selectin[i],(pos+9),(pos+11))
}
col_names = paste0(colnames(df), " <br/><em>(",type_cat,")</em><br/>", selectin)
isolate(DT::datatable(
current_stage$data,
options = list(
preDrawCallback = JS("function() {
Shiny.unbindAll(this.api().table().node()); }"),
drawCallback = JS("function() { Shiny.bindAll(this.api().table().node());
}")
),
colnames =col_names,
editable = TRUE,
escape=FALSE,
selection = list(target = 'column')))
}, server=FALSE)
output$log <- renderPrint({
updateData()
})
output$log2 <- renderPrint({
map_df(updateData(),class)
})
})
ui = shinyUI(
fluidPage(
titlePanel("My Awesome Shiny App"),
# Show a plot of the generated distribution
mainPanel(
DT::DTOutput("tableau"),
# Show log
verbatimTextOutput("log"),
verbatimTextOutput("log2")
)
)
)
runApp(list(ui = ui, server = server))
I think there is something wrong with the input button call "col1", "col2", etc..
I should maybe create them in a different way but I don't I am just stuck for the moment. Someone could give me an advice?
Kevin
Update:
I have tried this code but it looks to have some trouble with Ajax:
DataTables warning: table id=DataTables_Table_0 - Invalid JSON response. For more information about this error, please see http://datatables.net/tn/1"
library(DT)
library(shiny)
library(tidyverse)
server = shinyServer(function(input, output, session) {
current_stage <- reactiveValues(data=NULL, init=NULL,n=0)
mydata <- reactive({
df <- mtcars %>% rownames_to_column("model")
current_stage$data <- df
current_stage$init <- df
df
})
updateData = reactive({
# input$refresh
# df$ID <<- c(df$ID[n], df$ID[-n])
df = mydata()
if( !is.null(current_stage$data)){
df <- current_stage$data
map(1:ncol(df), function(i){
if(length(input[[paste0("col", i)]])>0){
if (input[[paste0("col", i)]]=="num"){
df[,i] <<- unlist(current_stage$init [,i]) %>% as.numeric
} else if (input[[paste0("col", i)]]=="cat"){
df[,i] <<- unlist(current_stage$init [,i]) %>% as.character
}
}
})}
# if(length(input[[paste0("col", 1)]])>0){
# if (input[[paste0("col", 1)]]=="num"){
# # message(proxy)
# df[,1] <- unlist(df[,1]) %>% as.numeric
# # # mydata()[,1] <- paste(proxy[,1],"ok")
# } else if (input[[paste0("col", 1)]]=="cat"){
# # # message(proxy)
# df[,1] <- unlist(df[,1]) %>% as.character
# }}
current_stage$data <- df
df
})
output$tableau <- DT::renderDataTable({
#df <- mtcars %>% rownames_to_column("model")
df <- mydata()
class <- map_df(df, typeof)
class <- gsub("double", "numeric", class)
class <- gsub("integer", "numeric", class)
tableSelectInput <- map(1:ncol(df),
function(i) {
if (class[i] =="numeric"){
opt1 <- "num"
opt2 <- "cat"
}else{
opt1 <- "cat"
opt2 <- "num"
}
selectInput(
inputId = paste0("col", i),
label = NULL, selected = opt1,
choices = c(opt1, opt2))
}
)
# I didn't find a more elegant way to turn '[[ ]]' in '[ ]'
l <- length(tableSelectInput)
selectin <- 1:l
type_cat <- 1:l
for (i in 1:l) {
selectin[i] = as.character(tableSelectInput[[i]])
pos=gregexpr("selected>",selectin[i])[[1]][1]
type_cat[i] = substr(selectin[i],(pos+9),(pos+11))
}
col_names = paste0( colnames(df), " <br/><em>(",type_cat,")</em><br/>", selectin)
DT::datatable(isolate(
updateData()),
options = list(
preDrawCallback = JS("function() {
Shiny.unbindAll(this.api().table().node()); }"),
drawCallback = JS("function() { Shiny.bindAll(this.api().table().node());
}")
),
colnames =col_names,
editable = TRUE,
escape=FALSE,
selection = list(target = 'column'))
}, server=FALSE)
proxy <- dataTableProxy('tableau')
observe({
replaceData(proxy, updateData(), resetPaging = TRUE )
})
output$log <- renderPrint({
updateData()
})
})
ui = shinyUI(
fluidPage(
titlePanel("My Awesome Shiny App"),
# Show a plot of the generated distribution
mainPanel(
DT::DTOutput("tableau"),
# Show log
verbatimTextOutput("log")
)
)
)
runApp(list(ui = ui, server = server))
I have a running example: I am updating a data.table depending on users input via checkboxes. So far Iam filtering the data explicitly, but I would like to do that with the help of a loop using a for loop or a function of the apply-family. Unfortunately I cannot get either to work.
library(shiny)
library(data.table)
library(DT)
tdata <- data.table(fruit = c(rep("Apple",4),rep( "Ban",4)),
bug1 = c(rep(c("+","+", "-","-"),2)),
bug2 = c(rep(c("+","-"),4)),
value = c(rep(c(0.25),4), 0.6,0.4,0,0))
ui <- (fluidPage(tagList(
sidebarLayout(
sidebarPanel(uiOutput("file_input")),
mainPanel(dataTableOutput('fruit_table'))
))))
server <- function(input, output) {
fileData <- reactive(
return(tdata)
)
colname_list <- reactive(
colnames(fileData())
)
output$file_input <- renderUI ({
if(is.null(fileData())){
return()
}else{
tagList(
lapply(1:(length(fileData())-1), function(i){
choice_list = unique(fileData()[,get(colnames(fileData()[,i, with = FALSE]))])
checkboxGroupInput(inputId = colnames(fileData()[,i, with = FALSE]),
label = colnames(fileData()[,i, with = FALSE]),
choices = choice_list,
inline = TRUE,
selected = fileData()[1, i, with = FALSE])
})
)
}
})
# works fine, but usually the number of columns changes so I want to keep it flexible
fruitFilter <- reactive({
fileData()[[paste0(colname_list()[1])]] %in% input[[paste0(colname_list()[1])]] &
fileData()[[paste0(colname_list()[2])]] %in% input[[paste0(colname_list()[2])]] &
fileData()[[paste0(colname_list()[3])]] %in% input[[paste0(colname_list()[3])]]
})
# fruitFilter <- reactive({
# for(i in 1: ((length(fileData()))-1)){
# fileData()[[paste0(colname_list()[i])]] %in% input[[paste0(colname_list()[i])]]
# }
# })
output$fruit_table <- renderDataTable({
datatable(fileData()[fruitFilter(),])
})
}
shinyApp(ui = ui, server = server)
I still consider myself a newby to Shiny. I appreciate any help! Thanks.
In the loop approach, we could initialize a list and then Reduce the output to a single logical vector
fruitFilter <- reactive({
i1 <- head(seq_along(fileData()), -1)
l1 <- vector('list', length(i1))
for(i in i1){
l1[[i]] <- fileData()[[colname_list()[i]]] %in% input[[colname_list()[i]]]
}
Reduce(`&`, l1)
})
With the full code
library(shiny)
library(data.table)
library(DT)
tdata <- data.table(fruit = c(rep("Apple",4),rep( "Ban",4)),
bug1 = c(rep(c("+","+", "-","-"),2)),
bug2 = c(rep(c("+","-"),4)),
value = c(rep(c(0.25),4), 0.6,0.4,0,0))
ui <- (fluidPage(tagList(
sidebarLayout(
sidebarPanel(uiOutput("file_input")),
mainPanel(dataTableOutput('fruit_table'))
))))
server <- function(input, output) {
fileData <- reactive(
return(tdata)
)
colname_list <- reactive(
colnames(fileData())
)
output$file_input <- renderUI ({
if(is.null(fileData())){
return()
}else{
tagList(
lapply(1:(length(fileData())-1), function(i){
choice_list = unique(fileData()[,get(colnames(fileData()[,i, with = FALSE]))])
checkboxGroupInput(inputId = colnames(fileData()[,i, with = FALSE]),
label = colnames(fileData()[,i, with = FALSE]),
choices = choice_list,
inline = TRUE,
selected = fileData()[1, i, with = FALSE])
})
)
}
})
fruitFilter <- reactive({
i1 <- head(seq_along(fileData()), -1)
l1 <- vector('list', length(i1))
for(i in i1){
l1[[i]] <- fileData()[[colname_list()[i]]] %in% input[[colname_list()[i]]]
}
Reduce(`&`, l1)
})
output$fruit_table <- renderDataTable({
datatable(fileData()[fruitFilter()])
})
}
shinyApp(ui = ui, server = server)
--output
I am trying to build a dynamic form where the user can add some criteria (via an actionButton) and select values for those criteria. When he's done selecting he may launch some computation.
Every criterion may be removed via a 'delete' button.
It works quite fine for all except the last inserted component that does not react to the related remove button.
The last component is removed only when the "Add criteria" button is clicked.
Is it a bug or could you point my mistake ?
I'm using an observeEvent with a renderUI to build components:
In server.R
observeEvent(input$go, {
output$ui <- renderUI({
rows <- lapply(names(components),buildComponent)
res = do.call(fluidRow, rows)
})
makeObservers()
})
makeObservers creates an observeEvent closure for every component :
makeObservers <- eventReactive(input$go, {
IDs <- names(components)
new_ind <- !(IDs %in% vals$y)
res <- lapply(IDs[new_ind], function (x) {
observeEvent(input[[paste0("rmv", x)]], {
if(components[[x]] == "Main1") removeComponent(x)
})
})
} ,
ignoreNULL = F, ignoreInit = F)
Please find a working example.
library(shiny)
library(shinythemes)
criterias <- c("Criteria 1", "Criteria 2", "Criteria 3", "Criteria 4")
components <<- list()
counter <<- 0
buildComponent <- function(val) {
idselect = paste0("select", val)
idremove <- paste0("rmv", val)
div(
selectInput(idselect, "criteria :", criterias, criterias[0]),
actionButton(idremove, paste0("X", val),icon = icon("remove"), size = "small")
)
}
removeComponent <- function(x) {
print(paste0("Removing" ,x))
xpath1 = paste0("div:has(> #select", x ,")" )
xpath2 = paste0("div:has(> #rmv", x ,")" )
removeUI(
selector = xpath1, multiple = T#, immediate=T
)
removeUI(
selector = xpath2, multiple = T#, immediate=T
)
components[[as.character(x)]] <<- NULL
}
ui <- shinyUI(fluidPage(
sidebarPanel(
actionButton("go", "Criteria", icon = icon("plus-circle"),
size = "small"),
uiOutput("ui")
),
mainPanel(
actionButton("activate", "show cpts"),
textOutput('show_components')
)
) )
server <- shinyServer(function(input, output, session) {
# Keep track of which observer has been already created
vals <- reactiveValues(y = NULL)
makeObservers <- eventReactive(input$go, {
IDs <- names(components)
new_ind <- !(IDs %in% vals$y)
print("new_ind")
print(IDs[new_ind])
# update reactive values
vals$y <- names(components)
res <- lapply(IDs[new_ind], function (x) {
observeEvent(input[[paste0("rmv", x)]], {
print(paste0("rmv", x))
print(components[[x]])
if(components[[x]] == "Main1") removeComponent(x)
})
})
} , ignoreNULL = F, ignoreInit = F)
observeEvent(input$go, {
output$ui <- renderUI({
print(counter)
counter <<- counter + 1
components[[as.character(counter)]] <<- "Main1"
print("adding component : ")
print(paste0(names(components),collapse = ";"))
rows <- lapply(names(components),buildComponent)
res = do.call(fluidRow, rows)
})
makeObservers()
})
observeEvent(input$activate, {
output$show_components <- renderPrint({
components
})
})
})
shinyApp(ui, server)
Thanks to great remarks from Mike Wise, i ve been able to spot the precise problem: (see comment in Mike answer). Here is some code :
library(shiny)
library(shinythemes)
criterias <- c("Criteria 1", "Criteria 2", "Criteria 3", "Criteria 4")
components <<- list()
counter <<- 0
buildComponent <- function(val) {
idselect = paste0("select", val)
idremove <- paste0("rmv", val)
div(
selectInput(idselect, "criteria :", criterias, criterias[0]),
actionButton(idremove, paste0("X", val),icon = icon("remove"), size = "small")
)
}
removeComponent <- function(x) {
print(paste0("Removing" ,x))
xpath1 = paste0("div:has(> #select", x ,")" )
xpath2 = paste0("div:has(> #rmv", x ,")" )
removeUI(
selector = xpath1, multiple = T#, immediate=T
)
removeUI(
selector = xpath2, multiple = T#, immediate=T
)
components[[as.character(x)]] <<- NULL
}
ui <- shinyUI(fluidPage(
sidebarPanel(
actionButton("go", "Criteria", icon = icon("plus-circle"),
size = "small"),
uiOutput("ui")
),
mainPanel(
actionButton("activate", "show cpts"),
textOutput('show_components')
)
) )
server <- shinyServer(function(input, output, session) {
# Keep track of which observer has been already created
vals <- reactiveValues(y = NULL)
makeObservers <- eventReactive(input$go, {
IDs <- names(components)
new_ind <- !(IDs %in% vals$y)
print("new_ind")
print(IDs[new_ind])
# update reactive values
vals$y <- names(components)
res <- lapply(IDs[new_ind], function (x) {
observeEvent(input[[paste0("rmv", x)]], {
print(paste0("rmv", x))
print(components[[x]])
if(components[[x]] == "Main1") removeComponent(x)
})
})
} , ignoreNULL = F, ignoreInit = F)
observeEvent(input$go, {
counter <<- counter + 1
components[[as.character(counter)]] <<- "Main1"
output$ui <- renderUI({
print(counter)
print("adding component : ")
print(paste0(names(components),collapse = ";"))
rows <- lapply(names(components),buildComponent)
res = do.call(fluidRow, rows)
})
makeObservers()
})
observeEvent(input$activate, {
output$show_components <- renderPrint({
components
})
})
})
shinyApp(ui, server)
Ok, there were some problems in the code, and I had to make some small but important changes to understand it, and then get it to work as intended. However it is essentially the same code.
Changes:
Changed rv$y to rv$prev_components.
Put your components and counter variable into the reactiveValues to get rid of the <<-, seeing as you were using reactiveValues already which obviates the need for <<-
Added a setdiff to find the new addition to your names (this was key).
Changed makeObervables into a simple function (it was not being used as an eventReactive at all anyway).
Probably a few other small things that are forgotten.
This is the code:
library(shiny)
library(shinythemes)
criterias <- c("Criteria 1", "Criteria 2", "Criteria 3", "Criteria 4")
vals <- reactiveValues(prev_components=list(),components=list(),counter=0)
buildComponent <- function(val) {
idselect = paste0("select", val)
idremove <- paste0("rmv", val)
div(
selectInput(idselect, "criteria :", criterias, criterias[0]),
actionButton(idremove, paste0("X", val),icon = icon("remove"), size = "small")
)
}
removeComponent <- function(x) {
print(paste0("Removing" ,x))
xpath1 = paste0("div:has(> #select", x ,")" )
xpath2 = paste0("div:has(> #rmv", x ,")" )
removeUI(
selector = xpath1, multiple = T#, immediate=T
)
removeUI(
selector = xpath2, multiple = T#, immediate=T
)
vals$components[[as.character(x)]] <<- NULL
}
ui <- shinyUI(fluidPage(
sidebarPanel(
actionButton("go", "Criteria", icon = icon("plus-circle"),
size = "small"),
uiOutput("uii")
),
mainPanel(
actionButton("activate", "show cpts"),
textOutput('show_components')
)
) )
server <- shinyServer(function(input, output, session) {
makeObservers <- function() {
IDs <- names(vals$components)
new_ind <- setdiff(IDs,vals$prev_components)
vals$prev_components <- names(vals$components)
res <- lapply(new_ind, function (x) {
observeEvent(input[[paste0("rmv", x)]], {
print(paste0("rmv", x))
print(vals$components[[x]])
if(vals$components[[x]] == "Main1") removeComponent(x)
})
})
}
observeEvent(input$go, {
print(vals$counter)
vals$counter <- vals$counter + 1
vals$components[[as.character(vals$counter)]] <- "Main1"
output$uii <- renderUI({
print("adding component : ")
print(paste0(names(vals$components),collapse = ";"))
rows <- lapply(names(vals$components),buildComponent)
res = do.call(fluidRow, rows)
})
makeObservers()
})
observeEvent(input$activate, {
output$show_components <- renderPrint({
vals$components
})
})
})
shinyApp(ui, server)
And a screen shot: