R Shiny - Flush dynamically generated input functions using selectInput - r

I have a Shiny app where my dynamically generated UI won't display properly once I change a selectInput value.
Here, you can choose between two data frames. When you click the button, it generates a selectInput (whose values are the column names of the data frame) and checkboxInput UI (the unique values of the column you've selected). That's good and all but once I change the data frame I want to view, the selectInput values populate "accordingly" with the column names of the new data frame. However, the checkboxInput no longer displays.
ui <- fluidPage(
fluidRow(
column(4,
uiOutput("projectSelection"),
uiOutput("addCol")
)
),
fluidRow(
tags$div(id="rowLabel")
)
)
server <- function(input, output, session) {
Project.ID <- c("Test Project 1", "Test Project 1", "Test Project 1", "Test Project 1")
Project.ID2 <- c("Test Project 2", "Test Project 2", "Test Project 2", "Test Project 2")
Author.ID <- c("1234", "5234", "3253", "5325")
Fav.Color <- c("Blue", "Red", "Blue", "Green")
Author.Name <- c("Bob", "Jenny", "Bob", "Alice")
output$projectSelection <- renderUI(
selectInput("projectSelection",
"Project Name:",
c("Project1", "Project2"),
selectize=TRUE)
)
# update datatable
project <- reactive({
if(input$projectSelection == "Project1"){
projectDT <- data.frame(Project.ID, Author.ID, Author.Name)
}
if(input$projectSelection == "Project2"){
projectDT <- data.frame(Project.ID2, Author.Name, Fav.Color)
}
return(projectDT)
})
#Button to add comparison column
output$addCol <- renderUI({
input$projectSelection #re-render once projectSelection changes?
if(is.null(input$projectSelection)) return()
actionButton('addCol', strong("Add UI"), icon=icon("plus", class=NULL, lib="font-awesome"))
})
observeEvent({input$addCol},{
insertUI(
selector = "#rowLabel",
where = "beforeEnd",
ui = div(
fluidRow(
column(4,
uiOutput(paste0("showMeta",input$addCol)),
uiOutput(paste0("showVal",input$addCol)),
br()
)
)
)
)
})
#Output creations
lapply(1:10, function(idx){
#comparison dropdowns
output[[paste0("showMeta",idx)]] <- renderUI({
input$projectSelection
selectInput(inputId = paste0("metalab",idx),
label = "Column Label:",
choices = c(unique(as.vector(colnames(project())))),
selectize = TRUE,
selected = input[[paste0("metalab",idx)]]
)
})
output[[paste0("showVal",idx)]] <- renderUI({
req(input$addCol >= idx)
input$projectSelection
if(!is.null(input[[paste0("metalab", idx)]])){
checkboxGroupInput(paste0("metaval",idx),
"Column Value:",
choices = unique(as.vector(unlist(project()[[input[[paste0("metalab",idx)]]]]))),
selected = input[[paste0("metaval",idx)]]
)
}
})
})
# observe({input$projectSelection},
# {
# lapply(1:10, function(idx){
# updateSelectInput(session, paste0("metalab",idx),
# label = "Column Label:",
# choices = c(unique(as.vector(colnames(project()))))
# )
# })
# })
}
shinyApp(ui=ui, server = server)
I'm not sure if there's a referencing issue somewhere along the line but I'd like for the checkboxInput UI to display with the appropriate values (for the selected column). I've thought about trying to have it re-render once input$projectSelection changes but that doesn't seem to do anything. I also tried putting an observe for it so the dynamically generated UI updates when input$projectSelection changes but I haven't been successful with that either.
I'd appreciate any and all help! Thanks!

Your checkbox only updated when you click the ADD button.
I've changed its code to make the dynamic checkbox according to the selectinput of the column names, I believe the functionality remains the same
ui <- fluidPage(
fluidRow(
column(4,
uiOutput("projectSelection"),
uiOutput("addSelect"),
uiOutput("checkbox")
)
),
fluidRow(
tags$div(id="rowLabel")
)
)
server <- function(input, output, session) {
Project.ID <- c("Test Project 1", "Test Project 1", "Test Project 1",
"Test Project 1")
Project.ID2 <- c("Test Project 2", "Test Project 2", "Test Project
2", "Test Project 2")
Author.ID <- c("1234", "5234", "3253", "5325")
Fav.Color <- c("Blue", "Red", "Blue", "Green")
Author.Name <- c("Bob", "Jenny", "Bob", "Alice")
output$projectSelection <- renderUI(
tagList(
selectInput("projectSelection",
"Project Name:",
c("Project1", "Project2"),
selectize=TRUE),
actionButton('addCol', strong("Add UI"), icon=icon("plus",
class=NULL, lib="font-awesome"))
)
)
# update datatable
project <- reactive({
if(input$projectSelection == "Project1"){
projectDT <- data.frame(Project.ID, Author.ID, Author.Name)
}
if(input$projectSelection == "Project2"){
projectDT <- data.frame(Project.ID2, Author.Name, Fav.Color)
}
return(projectDT)
})
observeEvent(input$addCol,{
output$addSelect <- renderUI({
selectInput("abc","Column Names", choices =
c(unique(as.vector(colnames(project())))))
})
output$checkbox <- renderUI({
checkboxGroupInput("cde", "Column Value", choices = project()
[,input$abc] )
})
})
}
shinyApp(ui=ui, server = server)

Related

How to clear the mainPanel if a selectInput choice has changed?

I am trying to create an app that will show you results depending on a selectInput and the changes are controlled by actionButtons.
When you launch the app, you have to select a choice: Data 1 or Data 2. Once you have selected your choice (e.g. Data 1), you have to click the actionButton "submit type of data". Next, you go to the second tab, choose a column and then click "submit".
The output will be: one table, one text and one plot.
Then, if you go back to the first tab and select "Data 2", everything that you have generated is still there (as it is expected, since you didn't click any button).
However, I would like to remove everything that is in the mainPanel if I change my first selectInput as you could see it when you launch the app for the first time.
The idea is that since you have changed your first choice, you will have to do the same steps again (click everything again).
I would like to preserve and control the updates with actionButtons as I have in my code (since I am working with really long datasets and I don't want to depend on the speed of loading things that I don't want until I click the button). Nevertheless, I cannot think a way to remove everything from mainPanel if I change the choice of the first selectInput.
Does anybody have an idea how I can achieve this?
Thanks in advance
Code:
library(shiny)
library(shinyWidgets)
library(shinyFeedback)
library(DT)
library(datasets)
ui <- fluidPage(
sidebarPanel(
tabsetPanel(id="histogram",
tabPanel("Selection",
useShinyFeedback(),
selectInput(inputId = "type", label = "Select your data",
choices = c("Data 1" = "data1",
"Data 2" = "data2")),
conditionalPanel(
condition = "input.type == 'data2'",
div(style = "position:absolute;right:2.5em;",
actionButton(
inputId = "button_more_info_data2",
label = "More info",
icon = icon("info-circle"))
)
),
actionButton(
inputId = "button",
label = "Submit type of data",
icon = icon("check")
)
),
tabPanel("Pick the column",
br(),
selectizeInput(inputId = "list_columns", label = "Choose the column:", choices=character(0)),
actionButton(
inputId = "button2",
label = "Submit")
))
),
mainPanel(
dataTableOutput("table"),
textOutput("text"),
plotOutput("myplot")
)
)
server <- function(input, output, session) {
observeEvent(input$type,{
feedbackWarning(inputId = "type",
show = ("data2" %in% input$type),
text ="This data is... Please, be careful..."
)
})
mydata <- reactive({
if(input$type == "data1"){
mtcars
}else{
iris
}
}) %>% bindEvent(input$button2)
# This is to generate the choices (gene list) depending on the user's input.
observeEvent(input$button, {
updateSelectizeInput(
session = session,
inputId = "list_columns",
choices = colnames(trees), options=list(maxOptions = length(colnames(trees))),
server = TRUE
)
})
output$table <- renderDataTable({
req(input$button2)
mydata()
})
output$text <- renderText({
req(input$button2)
input$list_columns
})
output$myplot <- renderPlot({
req(input$button2, input$button)
hist(trees[,input$list_columns])
})
}
if (interactive())
shinyApp(ui, server)
Here is an example using a reset button - using the selectInput you'll end up with a circular reference:
library(shiny)
library(shinyWidgets)
library(shinyFeedback)
library(DT)
library(datasets)
ui <- fluidPage(sidebarPanel(tabsetPanel(
id = "histogram",
tabPanel(
"Selection",
useShinyFeedback(),
selectInput(
inputId = "type",
label = "Select your data",
choices = c("Data 1" = "data1",
"Data 2" = "data2")
),
conditionalPanel(
condition = "input.type == 'data2'",
div(
style = "position:absolute;right:2.5em;",
actionButton(
inputId = "button_more_info_data2",
label = "More info",
icon = icon("info-circle")
)
)
),
actionButton(
inputId = "button",
label = "Submit type of data",
icon = icon("check")
),
actionButton(
inputId = "reset",
label = "Reset",
icon = icon("xmark")
)
),
tabPanel(
"Pick the column",
br(),
selectizeInput(
inputId = "list_columns",
label = "Choose the column:",
choices = character(0)
),
actionButton(inputId = "button2",
label = "Submit")
)
)),
mainPanel(
dataTableOutput("table"),
textOutput("text"),
plotOutput("myplot")
))
server <- function(input, output, session) {
observeEvent(input$type, {
feedbackWarning(
inputId = "type",
show = ("data2" %in% input$type),
text = "This data is... Please, be careful..."
)
})
mydata <- reactiveVal(NULL)
observe({
if (input$type == "data1") {
mydata(mtcars)
} else if (input$type == "data2") {
mydata(iris)
} else {
mydata(data.frame())
}
}) %>% bindEvent(input$button2)
observeEvent(input$reset, {
mydata(data.frame())
})
# This is to generate the choices (gene list) depending on the user's input.
observeEvent(input$button, {
updateSelectizeInput(
session = session,
inputId = "list_columns",
choices = colnames(trees),
options = list(maxOptions = length(colnames(trees))),
server = TRUE
)
})
output$table <- renderDataTable({
req(input$button2)
mydata()
})
output$text <- renderText({
req(input$button2)
input$list_columns
})
output$myplot <- renderPlot({
req(input$button2, input$button)
hist(trees[, input$list_columns])
})
}
shinyApp(ui, server)

How to reactivate the load function inside a shiny app?

I am currently loading the data set in a Shiny App using load function. I would like to know how I can generate it as a kind of load but make it reactive so that I can load one depending on the input that the user chooses.
Another problem is that within the rData the name of the data sets are the same, so when they are loaded they are lost since each time one is loaded the previous one is deleted.
I am looking for something like below.
Thank you
library(shiny)
ui <- fluidPage(
selectInput("data.set", "Select Data Set", c("Data set 1", "Data set 2"), "Data set 1" ),
uiOutput("ui1")
)
server <- function(input, output, session) {
if(input$data.set == "Data set 1"){
load("dataset1.RData")
}else{
load("dataset2.RData")
}
output$ui1 <- renderUI({
tags$div(
sidebarPanel(selectInput("input1", "select var 1", names(data), names(data)),
selectInput("input2", "select var 2", names(data), names(data))),
mainPanel(
plotOutput("plot")
)
)
output$plot <- renderPlot({
plot(data[,c(input$input1, input$input2)])
})
})
}
shinyApp(ui, server)
I think its best if you load the datasets into global namespace and use reactive to switch which one to show:
library(shiny)
ui <- fluidPage(
selectInput("data.set", "Select Data Set", c("Data set 1", "Data set 2"), "Data set 1" ),
uiOutput("ui1")
)
data1 <- mtcars #load("dataset1.RData")
data2 <- iris #load("dataset2.RData")
server <- function(input, output, session) {
data <- eventReactive(input$data.set,{
if(input$data.set == "Data set 1"){
data1
}else{
data2
}
})
output$ui1 <- renderUI({
tags$div(
sidebarPanel(selectInput("input1", "select var 1", names(data()), names(data())),
selectInput("input2", "select var 2", names(data()), names(data()))),
mainPanel(
plotOutput("plot")
)
)
})
output$plot <- renderPlot({
cols <- c(unique(c(input$input1, input$input2)))
plot(data()[,cols])
})
}
shinyApp(ui, server)
Here is a solution using reactive values.
library(shiny)
ui <- fluidPage(
selectInput("data.set", "Select Data Set",
choices = c(`Data set 1` = "dataset1.RData",
`Data set 2` = "dataset2.RData"),
selected = c(`Data set 1` = "dataset1.RData")),
uiOutput("ui1")
)
server <- function(input, output, session) {
r <- reactiveValues(
data = NULL
)
observeEvent(input$data.set, {
data_name <- load(input$data.set)
r$data <- get(data_name)
})
output$ui1 <- renderUI({
tags$div(
sidebarPanel(selectInput("input1", "select var 1", names(r$data)),
selectInput("input2", "select var 2", names(r$data))),
mainPanel(
plotOutput("plot")
)
)
})
output$plot <- renderPlot({
if (input$input1 %in% names(r$data) && input$input2 %in% names(r$data)) {
plot(r$data[,c(input$input1, input$input2)])
}
})
}
shinyApp(ui, server)

Data frame won't update using observeEvent in Shiny R

I am a novice programmer, please excuse if I am not clear or missing relevant information. I have a shiny app written that imports a dataframe from another set of code. I would like to update that dataframe with user input in the app. I have gotten this to work where I upload the dataframe as a reactive variable using the code below:
DATA
current.shiny <- data.frame("Task" = as.character(c("Task 1", "Task 2", "Task 3")), "Completed" = as.character(c("Yes", "NO", "Yes")),"Date.Completed" = as.Date(c("2020-10-19","2020-10-20", "2020-10-21")))
UI
ui<- fluidPage(
# Application title
titlePanel("Week of 11.02.2020"),
# Sidebar with reactive inputs
sidebarLayout(
sidebarPanel(
selectInput(inputId = "task.choice", label = "Task",
choices = c(as.list(current.shiny$Task))),
selectInput(inputId = "completed", label = "Completed?",
choices = c("Yes" = "Yes", "No" = "No")),
dateInput(inputId = "date.completed", label ="Date Completed"),
actionButton("update","Update Sheet")
),
# a table of reactive outputs
mainPanel(
mainPanel(
#DT::dataTableOutput("dt_table", width = 500)
)
)
),
# column(12,
# DT::dataTableOutput("data", width = "100%")),
column(12,
DT::dataTableOutput("xchange", width = "100%"))
)
SERVER 1
server <- function(input, output) {
# # Re-read data for any changes, write to csv new changes, ignore startup
observeEvent(input$update,{
test.data<-current.shiny
test.data$Completed[test.data$Task == input$task.choice]<-as.character(input$completed)
ignoreInit=T
})
# #Reactive variable xchange that updates the values of data
xchange<-reactive({
test.data<-current.shiny
test.data$Completed[test.data$Task == input$task.choice]<-as.character(input$completed)
test.data$Date.Completed[test.data$Task == input$task.choice]<-as.Date(input$date.completed)
test.data
})
#Display the most recent file, with the most recent changes
output$xchange <- renderDataTable({
datatable(xchange(), options = list(dom = "t"))
})
}
shinyApp(ui,server)
This works to a degree. However, it is over-reactive in that I need it to only update the table once a button is pressed. The observeEvent function in the above code doesn't seem to do anything (it was mostly copy/pasted from another stack overflow thread). I've tried to set this up below, but I cannot get it to run.
SERVER 2
server <- function(input, output, session) {
rxframe <- reactiveVal(
as.data.frame(current.shiny)
)
observeEvent(input$update, {
rxframe$Completed[rxframe$Task == input$task.choice]<-as.character(input$completed)
})
output$xchange <- shiny::renderTable( rxframe() )
}
shinyApp(ui, server)
Can anyone see some way that I am calling the observeEvent incorrectly that is causing it to not function properly? Any insight would be greatly appreciated, I've been stuck on this for some time.
Your code directly reacted to every change because you are using reactive.
If you want to delay the reaction you can use observeEvent along with reactiveValues or eventReactive.
Here is an example using reactiveVal and observeEvent:
library(shiny)
library(DT)
current.shiny <- data.frame(
"Task" = as.character(c("Task 1", "Task 2", "Task 3")),
"Completed" = as.character(c("Yes", "NO", "Yes")),
"Date.Completed" = as.Date(c("2020-10-19", "2020-10-20", "2020-10-21"))
)
ui <- fluidPage(
# Application title
titlePanel("Week of 11.02.2020"),
# Sidebar with reactive inputs
sidebarLayout(
sidebarPanel(
selectInput(
inputId = "task.choice",
label = "Task",
choices = c(as.list(current.shiny$Task))
),
selectInput(
inputId = "completed",
label = "Completed?",
choices = c("Yes" = "Yes", "No" = "No")
),
dateInput(inputId = "date.completed", label = "Date Completed"),
actionButton("update", "Update Sheet")
),
mainPanel(column(
12,
DT::dataTableOutput("xchangeOut", width = "100%")
))
))
server <- function(input, output) {
xchange <- reactiveVal(current.shiny)
observeEvent(input$update, {
test.data <- xchange()
test.data$Completed[test.data$Task == input$task.choice] <-input$completed
test.data$Date.Completed[test.data$Task == input$task.choice] <- input$date.completed
xchange(test.data)
# write.csv
})
#Display the most recent file, with the most recent changes
output$xchangeOut <- renderDataTable({
datatable(xchange(), options = list(dom = "t"))
})
}
shinyApp(ui, server)

Access input ID of reactive radioButtons in shiny app

I am trying to create a shiny app which includes radioButtons which are reactive to some user input.
I was successful to implement the code from this related question:
Add n reactive radioButtons to shiny app depending on user input
However, in this question it is not described how to access this values.
Here is the example:
server.R
library(shiny)
shinyServer( function(input, output, session) {
output$variables <- renderUI({
numVar <- length(as.integer(input$in0))
lapply(input$in0, function(x) {
list(radioButtons(paste0("dynamic",x), x,
choices = c("Choice one" = "one",
"Choice two" = "two"), selected = "one"))
})
})
})
ui.R
library(shiny)
shinyUI(pageWithSidebar (
headerPanel("mtcars subset"),
sidebarPanel(
selectInput(inputId = 'in0', label = 'Choose variables',
choices = colnames(mtcars),
multiple = TRUE, selectize = TRUE),
uiOutput("variables")
),
mainPanel()
))
What I have tried so far:
numVar <- length(as.integer(input$in0))
for(i in 1:numVar){
in <- noquote(paste0("dynamic",input$in0[i]))
input$in
}
However, this does not work. Any suggestions?
I'm not sure exactly of your use case but to access the values you could edit your code as below:
numVar <- length(as.integer(input$in0))
for(i in 1:numVar){
value <- paste0("dynamic",input$in0[i])
input[[value]]
}
Basically you need to use input[[value]] as opposed to input$value in this case. It doesn't seem that R allows you to use in as a variable (probably because it's already used in other contexts). You don't need noquote() anymore.
Welcome to stackoverflow!
You were almost there. However, you'll have to make sure, that you are accessing the inputs in a reactive context.
Here is a working example:
library(shiny)
ui <- fluidPage(
pageWithSidebar (
headerPanel("mtcars subset"),
sidebarPanel(
selectInput(inputId = 'in0', label = 'Choose variables',
choices = colnames(mtcars),
multiple = TRUE, selectize = TRUE),
uiOutput("variables")
),
mainPanel(
textOutput("myChoicesDisplay")
)
)
)
server <- function(input, output, session) {
output$variables <- renderUI({
lapply(input$in0, function(x) {
list(radioButtons(paste0("dynamic", x), x,
choices = c("Choice one" = "one",
"Choice two" = "two"), selected = "one"))
})
})
myChoices <- reactive({
dynInputList <- list()
for(dynInputs in paste0("dynamic", input$in0)){
dynInputList[[dynInputs]] <- input[[dynInputs]]
}
return(dynInputList)
})
output$myChoicesDisplay <- renderText({
paste(input$in0, myChoices(), sep = ": ", collapse = ", ")
})
}
shinyApp(ui, server)

re-hide conditional shiny output once it has been rendered

I need some help as to how to re-hide a shiny output once it has been rendered. Below I have provided a reproducible example to explain my question.
I want text 2.2 to only be shown if Option 1 and B are selected, and text 1 to only show when option 2 is selected. I have done this by including conditionalPanel() with the conditions set accordingly.
This works, however, once the text has been rendered this text will not disappear when the input changes. I want text 2.2 to disappear if the user then changes the inputs to select any other option i.e. chooses Option 2.
Is it possible to do this with shiny? Apologies if this has been asked before - I couldn't find anything through searching - your help is much appreciated!
library(shiny)
ui <- fluidPage(
sidebarPanel(
selectInput("Input1", label = "Input1", choices = c("Option 1", "Option 2") ),
conditionalPanel(condition = "input.Input1 == 'Option 1'",
selectInput("Input2", label = "Input2",
choices = c("A", "B"))),
),
mainPanel(
tabsetPanel(
tabPanel("Tab 1", textOutput(outputId = "text1")),
tabPanel("Tab 2", textOutput(outputId = "text2.1"), textOutput(outputId = "text2.2") )
)
)
)
server <- function(input, output) {
observe({if(input$Input1 == 'Option 2'){
output$text1 <- renderText("This text only shows for option 2")
}})
output$text2.1 <- renderText("some text")
observe({if(input$Input2 == 'B'){
output$text2.2 <- renderText("Show this only if option 1B is selected")
}})
}
shinyApp(ui, server)
You need to specify the different if possiblities inside the observe environment. Here's a solution:
library(shiny)
ui <- fluidPage(
sidebarPanel(
selectInput("Input1", label = "Input1", choices = c("Option 1", "Option 2") ),
conditionalPanel(condition = "input.Input1 == 'Option 1'",
selectInput("Input2", label = "Input2",
choices = c("A", "B"))),
),
mainPanel(
tabsetPanel(
tabPanel("Tab 1", textOutput(outputId = "text1")),
tabPanel("Tab 2", textOutput(outputId = "text2.1"), textOutput(outputId = "text2.2") )
)
)
)
server <- function(input, output) {
observe({
if(input$Input1 == 'Option 2'){
output$text1 <- renderText("This text only shows for option 2")
}
else {
output$text1 <- renderText("")
}
})
output$text2.1 <- renderText("some text")
observe({
if (input$Input2 == "B") {
output$text2.2 <- renderText("Show this only if option 1B is selected")
}
else {
output$text2.2 <- renderText("")
}
})
}
shinyApp(ui, server)

Resources