Using two reactives in shiny that depend on each other - r

I have been trying to create a dashboard with up to 3 inputs and then plot some data. I have done this part but the requirement now has changed that every time there is a selection of a new variable they should also be able to filter the data based on the new input. Here has been my attempt so far:
UI:
library(shiny)
ui <- fluidPage(
sidebarPanel(
tags$br(),
uiOutput("textbox_ui"),
uiOutput("filter_ui"),
tags$br(),
actionButton("add_btn", "Add Factor"),
actionButton("rm_btn", "Remove Factor"),
tags$br(),
actionButton("make","Create Graph and Tables")
),
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("Data stuff")
)
)
)
Server:
server <- function(input, output) {
# Track the number of input boxes to render
counter <- reactiveValues(n = 0)
AllInputs <- reactive({
x <- reactiveValuesToList(input)
})
observeEvent(input$add_btn, {
if(counter$n >2){
2
}else{
counter$n <- counter$n + 1
}
})
observeEvent(input$rm_btn, {
if (counter$n > 0) counter$n <- counter$n - 1
})
textboxes <- reactive({
n <- counter$n
if (n > 0) {
isolate({
lapply(seq_len(n), function(i) {
selectInput(inputId = paste0("var", i+1),
label = "",
choices = colnames(mtcars),
selected = AllInputs()[[paste0("var", i+1)]])
})
})
}
})
filterboxes <- reactive({
n <- counter$n
extrainputs <- sapply(seq_len(n), function(i) {
AllInputs()[[paste0("var", i+1)]]
})
summvar <- c(input$var1, extrainputs)
if(n > 0 ){
isolate({
lapply(1:length(summvar), function(x){
text <- summvar[x]
val_level <- unique(mtcars[[text]])
selectInput(inputId = paste0("fil",x+1),
label = paste0("Filter for ", text),
choices = val_level,
multiple = TRUE,
selected = val_level)
})
})
}
})
output$textbox_ui <- renderUI({ textboxes() })
output$filter_ui <- renderUI({ filterboxes() })
}
Two problems arise with this set up so far. One I cannot unselect any of the values when they appear in the filter second I see this warning on the sever side "Warning: Error in .subset2: invalid subscript type 'list'". My reactive skills are quite poor and any suggestions (reactive or not) would be appreciated.

As suggested in my comment...
library(shiny)
myfun <- function(df, var1) {
df %>% mutate(newvar = !!sym(var1)) # create newvar
}
ui <- fluidPage(
sidebarPanel(
tags$br(),
# uiOutput("textbox_ui"),
# uiOutput("filter_ui"),
tags$br(),
tags$div(id = 'placeholder'),
actionButton("add_btn", "Add Factor"),
actionButton("removeBtn", "Remove Factor"),
tags$br(),
actionButton("make","Create Graph and Tables")
),
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("Data stuff")
)
)
)
server <- function(input, output, session) {
# Track the number of variables
numvars <- reactiveVal(0)
### keep track of elements/lines inserted and not yet removed
inserted <- c()
observeEvent(input$add_btn, {
if(input$add_btn==0) {
return(NULL)
}
else {
if (numvars()<0) {
numvars(0) # clicking on remove button too many times yields negative number; reset it to zero
}
newValue <- numvars() + 1 # newValue <- rv$numvars + 1
numvars(newValue) # rv$numvars <- newValue
# btn needs to be adjusted if removing and adding factors
if (input$removeBtn==0){
btn <- input$add_btn
}else {
if (input$add_btn > input$removeBtn) {
btn <- input$add_btn - input$removeBtn # add_btn counter does not decrease
}else btn <- numvars()
}
id <- paste0('txt', btn)
insertUI(
selector = '#placeholder',
## wrap element in a div with id for ease of removal
ui = tags$div(
selectInput(inputId = paste0("var", btn),
label = "",
choices = colnames(mtcars)
),
selectInput(inputId = paste0("fil",btn),
label = paste0("Filter for ", id),
choices = "",
multiple = TRUE),
id = id
)
)
}
# inserted <<- c(id, inserted) ## removes first one first
inserted <<- c(inserted, id) ## removes last one first
}, ignoreInit = TRUE) ## end of observeevent for add_btn
observe({
#print(numvars())
lapply(1:numvars(), function(i){
observeEvent(input[[paste0("var",i)]], {
mydf <- mtcars
mydf2 <- myfun(mydf,input[[paste0("var",i)]])
mysub <- unique(mydf2$newvar)
nam <- as.character(input[[paste0("var",i)]])
updateSelectInput(session = session,
inputId = paste0("fil",i),
label = paste0("Filter for ", nam),
choices = mysub,
selected = mysub
)
})
})
})
observeEvent(input$removeBtn, {
newValue <- numvars() - 1
numvars(newValue)
removeUI(
## pass in appropriate div id
selector = paste0('#', inserted[length(inserted)])
)
inserted <<- inserted[-length(inserted)]
print(inserted)
}, ignoreInit = TRUE)
}
shinyApp(ui = ui, server = server)

Related

How can I put the conditional while rendering the datatable in rshiny

I am trying to achieve following steps while working on the rshiny :
1: creating dynamic tabs on click of the cell : DONE
2: creating dynamic subtabs on click of the parent tab : DONE
3: need to render the datatable based on the following condition :
if ( are matching or == ) then display the data accordingly.
please find the below code for your reference :
library(shiny)
library(DT)
library(shinyWidgets)
shinyApp(
ui <- fluidPage(
headerPanel("Product Details"),
mainPanel(
# Output: Tabset w/ plot, summary, and table ----
tabsetPanel(type = "tabs", id="myTabs",
tabPanel("Company Details", DT::dataTableOutput("data")),
)
)
),
server <- function(input, output, session) {
readXLSXFile <- readxl::read_excel(paste("sample_data.xlsx"),1)
data <- head(readXLSXFile)
tabIndex <- reactiveVal(0)
myValue <- reactiveValues(companyDetails = '')
shinyInput <- function(FUN, len, id, ...) {
inputs <- character(len)
for (i in seq_len(len)) {
inputs[i] <- as.character(FUN(paste0(id, i), ...))
}
inputs
}
df <- reactiveValues(data = data.frame(
DealID = data[1],
Details = shinyInput(actionButton, length(data)+1,
'button_', label = "Edit",
onclick = 'Shiny.onInputChange(\"select_button\", this.id)',
style = "color: black;
background-color: white",
class="btn-success",
#icon = icon("edit")
),
Tickers = data[3],
stringsAsFactors = FALSE
# row.names = 1:length(data)
))
output$data <- DT::renderDataTable(
df$data, server = FALSE, escape = FALSE, selection = 'none'
)
observeEvent(input$select_button, {
selectedRow <- as.numeric(strsplit(input$select_button, "_")[[1]][2])
myValue$companyDetails <<- paste('click on ',df$data[selectedRow,1])
stringVal <- c(unlist(strsplit(df$data[selectedRow,3],",")))
topTabValue <- c(df$data[selectedRow,1])
subTabData <- c()
datafromsecondRow <- c(data[2][1])
subTabDataOutput <- c()
data_frame_mod <- c()
appendTab("myTabs",
tabPanel(topTabValue,br(),
actionButton("removeTab", "Remove this Tab", icon = icon("remove")),br(),br(),
tabsetPanel(type="tabs", id=c(topTabValue)
),
),
# select=TRUE
)
lapply(1:length(stringVal), function(i) {
subTabData = stringVal[i]
readXLSXFileSheetTwo <- readxl::read_excel(paste("sample_data.xlsx"),2)
dataFileTwo <- head(readXLSXFileSheetTwo)
# print(c(dataFileTwo$Ticker) %in% c(subTabData))
# print("+++++++++++++++++++")
# print(subTabData)
appendTab(c(topTabValue),
tabPanel(subTabData, br(),
tags$h5(paste("You are at -> ",subTabData)),
output$subTabData <- DT::renderDataTable({
dataFileTwo[c(dataFileTwo$Ticker) %in% c(subTabData),TRUE]
datatable(dataFileTwo, options = list(dom = 'ft'),escape=FALSE)
})
),
# print(c(subTabData))
)
observeEvent(input$subTabData, {
appendTab(subTabData,
tabPanel(topTabValue,br(),
actionButton("removeTab", "Remove this Tab", icon = icon("remove")),br(),br(),
tabsetPanel(type="tabs", id=c(topTabValue)
),
),
)
})
})
})
observeEvent(input$removeTab, {
removeTab("myTabs", target=input$myTabs)
})
output$myText <- renderText({
myValue$companyDetails
})
}
)
Please help me to solve this point.
output$subTabData <- DT::renderDataTable({
**dataFileTwo[c(dataFileTwo$Ticker) %in% c(subTabData),TRUE]**
datatable(dataFileTwo, options = list(dom = 'ft'),escape=FALSE)
})
It is still rendering the whole dataset.. I stuck on conditional render the data on click of subtab.

How to render a list of dataframes as tables to show as output in Shiny

I am working in a shiny app to compare multiple items according to an input defined by the user. The code works fine but I have an issue. I do not know what function I should apply in order to display the results of some computing as tables in the right side of the app. The code of the app is next:
library(shiny)
library(shinydashboard)
#Function
compute <- function(firstitem,seconditem)
{
Sum <- firstitem+seconditem
Difference <- firstitem+seconditem
Product <- firstitem*seconditem
Ratio <- firstitem/seconditem
Res <- data.frame(C1=Sum,C2=Difference,C3=Product,C4=Ratio)
return(Res)
}
#App
ui = shinyUI(fluidPage(
titlePanel("Compare"),
sidebarLayout(
sidebarPanel(
numericInput("numitems", label = "Number of items to compare?",
min = 1, max = 5, value = 1),
uiOutput("period_cutpoints"),
uiOutput("period_cutpoints2"),
actionButton("submit", "Submit")
),
mainPanel(
textOutput("numitems"),
textOutput("cutpoints")
)
)
))
server = shinyServer(function(input, output, session) {
output$period_cutpoints<-renderUI({
req(input$numitems)
lapply(1:(input$numitems), function(i) {
numericInput(inputId=paste0("firstitem",i),
label=paste0("Enter the value of first item ", i, ":"),value = 0)
})
})
output$period_cutpoints2<-renderUI({
req(input$numitems)
lapply(1:(input$numitems), function(i) {
numericInput(inputId=paste0("seconditem",i),
label=paste0("Enter the value of second item ", i, ":"),value = 0)
})
})
seldates <- reactiveValues(x=NULL)
observeEvent(input$submit, {
seldates$x <- list()
lapply(1:(input$numitems), function(i) {
seldates$x[[i]] <- compute(firstitem = input[[paste0("firstitem", i)]],seconditem = input[[paste0("seconditem", i)]])
})
})
output$cutpoints <- renderText({as.character(seldates$x)})
})
shinyApp(ui = ui, server = server)
It is working but my issue is that I do not know how to set the content of seldates, which are dataframes, as tables that should appear one after another. This task is done with output$cutpoints but I can not get them as Tables:
Does anybody know how can I fix this issue? Many thanks!
Try this
library(shiny)
library(shinydashboard)
library(DT)
#Function
compute <- function(firstitem,seconditem)
{
Sum <- firstitem+seconditem
Difference <- firstitem+seconditem
Product <- firstitem*seconditem
Ratio <- firstitem/seconditem
Res <- data.frame(C1=Sum,C2=Difference,C3=Product,C4=Ratio)
return(Res)
}
#App
ui = shinyUI(fluidPage(
titlePanel("Compare"),
sidebarLayout(
sidebarPanel(
numericInput("numitems", label = "Number of items to compare?",
min = 1, max = 5, value = 1),
uiOutput("period_cutpoints"),
uiOutput("period_cutpoints2"),
actionButton("submit", "Submit")
),
mainPanel(
textOutput("numitems"),
textOutput("cutpoints"),
uiOutput("t1")
)
)
))
server = shinyServer(function(input, output, session) {
output$period_cutpoints<-renderUI({
req(input$numitems)
lapply(1:(input$numitems), function(i) {
numericInput(inputId=paste0("firstitem",i),
label=paste0("Enter the value of first item ", i, ":"),value = i)
})
})
output$period_cutpoints2<-renderUI({
req(input$numitems)
lapply(1:(input$numitems), function(i) {
numericInput(inputId=paste0("seconditem",i),
label=paste0("Enter the value of second item ", i, ":"),value = i+i)
})
})
seldates <- reactiveValues(x=NULL)
observeEvent(input$submit, {
seldates$x <- list()
lapply(1:(input$numitems), function(i) {
seldates$x[[i]] <- compute(firstitem = input[[paste0("firstitem", i)]],seconditem = input[[paste0("seconditem", i)]])
})
})
output$cutpoints <- renderText({as.character(seldates$x)})
observeEvent(input$submit, {
lapply(1:(input$numitems), function(i) {
output[[paste0("table",i)]] <- renderDT(seldates$x[[i]])
})
output$t1 <- renderUI({
tagList(
lapply(1:(input$numitems), function(i) {
DTOutput(paste0("table",i))
})
)
})
})
})
shinyApp(ui = ui , server = server)

CheckboxInput with Edit table in DT R Shiny

I tried to combine editing table by adding, deleting row in DT table with checkboxInput(). It is not quite correct.
If I didn't add editing feature, it returned correct, but if I added editing feature,it didn't response after I added another row. I got stuck for a while, I will appreciate any help from you guys
library(shiny)
library(shinyjs)
library(DT)
# Tab 2 UI code.
tab2UI <- function(id) {
ns <- NS(id)
tabPanel(
"Tab 2",
fluidRow(
#uiOutput(ns('cars')),
h2('The mtcars data'),
DT::dataTableOutput(ns('mytable2')),
uiOutput(ns("edit_1")),
h2("Selected"),
tableOutput(ns("checked"))
)
)
}
# Tab 2 server code.
tab2Server <- function(input, output, session) {
ns <- session$ns
# Helper function for making checkboxes.
shinyInput = function(FUN, len, id, ...) {
inputs = character(len)
for (i in seq_len(len)) {
inputs[i] = as.character(FUN(ns(paste0(id, i)), label = NULL, ...))
}
inputs
}
# Update table records with selection.
subsetData <- reactive({
sel <- mtcars[1:5,]
})
values <- reactiveValues(df = NULL)
observe({
values$df <- subsetData()
})
# Datatable with checkboxes.
output$mytable2 <- DT::renderDataTable(
datatable(
data.frame(values$df,Favorite=shinyInput(checkboxInput,nrow(values$df), "cbox_", width = 10)),
editable = TRUE,
selection = 'single',
escape = FALSE,
options = list(
paging = FALSE,
preDrawCallback = JS('function() {Shiny.unbindAll(this.api().table().node()); }'),
drawCallback = JS('function() {Shiny.bindAll(this.api().table().node()); }')
)
)
)
observeEvent(input$add.row_1,{
# print(paste0("Row selected",input$mytable2_rows_selected))
if (!is.null(input$mytable2_rows_selected)) {
td <- values$df
tid_n = as.numeric(input$mytable2_rows_selected)
tid = as.numeric(input$mytable2_rows_selected) + 1
if(tid_n == nrow(td)){
td<- rbind(data.frame(td[1:tid_n, ]),
data.frame(td[tid_n, ]))
}else{
td<- rbind(data.frame(td[1:tid_n, ]),
data.frame(td[tid_n, ]),
data.frame(td[tid: nrow(td), ]))
}
td <- data.frame(td)
print(td)
values$df <- td
}
})
output$edit_1 <- renderUI({
tagList(
actionButton(inputId = ns("add.row_1"), label = "Add Row", icon = icon("plus"),class = "example-css-selector",style = "background-color:gray; border-color:gray;color:white;height:31px;"),
actionButton(inputId = ns("delete.row_1"), label = "Delete Row", icon = icon("minus"),class = "example-css-selector",style = "background-color:gray; border-color:gray;color:white;height:31px;"),br(),br()
)
})
# Helper function for reading checkbox.
shinyValue = function(id, len) {
values <- unlist(lapply(seq_len(len), function(i) {
value = input[[paste0(id, i)]]
if (is.null(value)) NA else value
}))
return(values)
}
# Output read checkboxes.
observe({
len <- nrow(values$df)
output$checked <- renderTable({
data.frame(selected=shinyValue("cbox_", len))
})
})
}
# Define UI for application.
ui <- fluidPage(
useShinyjs(),
navbarPage(
'Title',
tab2UI("tab2")
)
)
# Define server.
server <- function(input, output, session) {
# Call tab2 server code.
callModule(tab2Server, "tab2")
}
# Run the application
shinyApp(ui = ui, server = server)

Incorrect subset of dataframe based on dynamic column names selection

I have the shiny app below in which the user may select between one or more column names from the data frame.
name<-c("John","Jack","Bill")
value1<-c(2,4,6)
add<-c("SDF","GHK","FGH")
value2<-c(3,4,5)
dt<-data.frame(name,value1,add,value2)
Then for every selection he makes the relative pickerInput() may be displayed below. Then based on the selection of column or columns and their values I would like to subset the initial dataframe and display it in a table. But the name of the columns may differ for every different dataframe Im going to use in my orifinal app so I need a more generic way to do it. My method is below but there are some things that do not work. For example if I select name(without any name selected) and the value1 which has all the values selected I get an empty table while I should have had all the values. The table is starting to get filled when I start selecting names.
library(DT)
# ui object
ui <- fluidPage(
titlePanel(p("Spatial app", style = "color:#3474A7")),
sidebarLayout(
sidebarPanel(
pickerInput(
inputId = "p1",
label = "Select Column headers",
choices = colnames( dt),
multiple = TRUE,
options = list(`actions-box` = TRUE)
),
#Add the output for new pickers
uiOutput("pickers")
),
mainPanel(
DTOutput("table")
)
)
)
# server()
server <- function(input, output) {
observeEvent(input$p1, {
#Create the new pickers
output$pickers<-renderUI({
div(lapply(input$p1, function(x){
if (is.numeric(dt[[x]])) {
sliderInput(inputId=x, label=x, min=min(dt[x]), max=max(dt[[x]]), value=c(min(dt[[x]]),max(dt[[x]])))
}
else if (is.factor(dt[[x]])) {
selectInput(
inputId = x#The colname of selected column
,
label = x #The colname of selected column
,
choices = dt[,x]#all rows of selected column
,
multiple = TRUE
)
}
}))
})
})
output$table<-renderDT({
req(input$p1, sapply(input$p1, function(x) input[[x]]))
dt_part <- dt
for (colname in input$p1) {
if (is.factor(dt_part[[colname]])) {
dt_part <- subset(dt_part, dt_part[[colname]] %in% input[[colname]])
} else {
dt_part <- subset(dt_part, (dt_part[[colname]] >= input[[colname]][[1]]) & dt_part[[colname]] <= input[[colname]][[2]])
}
}
dt_part
})
}
# shinyApp()
shinyApp(ui = ui, server = server)
By default, inputs where no value is selected are NULL. So you you have to check if the input is NULL and then don't filter. If you use dplyr, I've recently written a function to make this filtering easier in shiny.
Here is a working example with your code:
library(shiny)
library(DT)
library(shinyWidgets)
# ui object
ui <- fluidPage(
titlePanel(p("Spatial app", style = "color:#3474A7")),
sidebarLayout(
sidebarPanel(
pickerInput(
inputId = "p1",
label = "Select Column headers",
choices = colnames( dt),
multiple = TRUE,
options = list(`actions-box` = TRUE)
),
#Add the output for new pickers
uiOutput("pickers")
),
mainPanel(
DTOutput("table")
)
)
)
# server()
server <- function(input, output) {
observeEvent(input$p1, {
#Create the new pickers
output$pickers<-renderUI({
div(lapply(input$p1, function(x){
if (is.numeric(dt[[x]])) {
sliderInput(inputId=x, label=x, min=min(dt[x]), max=max(dt[[x]]), value=c(min(dt[[x]]),max(dt[[x]])))
}
else if (is.factor(dt[[x]])) {
selectInput(
inputId = x#The colname of selected column
,
label = x #The colname of selected column
,
choices = dt[,x]#all rows of selected column
,
multiple = TRUE
)
}
}))
})
})
output_table <- reactive({
req(input$p1, sapply(input$p1, function(x) input[[x]]))
dt_part <- dt
for (colname in input$p1) {
if (is.factor(dt_part[[colname]]) && !is.null(input[[colname]])) {
dt_part <- subset(dt_part, dt_part[[colname]] %in% input[[colname]])
} else {
if (!is.null(input[[colname]][[1]])) {
dt_part <- subset(dt_part, (dt_part[[colname]] >= input[[colname]][[1]]) & dt_part[[colname]] <= input[[colname]][[2]])
}
}
}
dt_part
})
output$table<-renderDT({
output_table()
})
}
# shinyApp()
shinyApp(ui = ui, server = server)

Shiny : dynamic form/ui : last observer in a list does not trigger removeUI

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:

Resources