Make Shiny SelectInput allow for multiple duplicated values to be selected - r

I would like to allow the user to select multiple times the same choice. It implies that when the user selects some element, it should not be removed from the choices' dropdown menu.
Here is a minimal reproducible example:
library(shiny)
ui <- fluidPage(
selectInput(
inputId = "ManyDuplicated",
label = 'SelectInput',
choices = c('Hello', 'World'),
selected = NULL,
multiple = TRUE
)
)
server <- function(input, output) {
}
shinyApp(ui = ui, server = server)
How it is:
How I would like it to be:
What I tried and may be of help:
This code (https://github.com/rstudio/shiny/issues/2939#issuecomment-678269674) works perfectly for a single choice ("^" in the case). However, I can't make it work for more choices (c("^", "a"), for example).
library(shiny)
ui <- fluidPage(
selectInput("x", "choose", c("^" = 1), multiple = TRUE)
)
server <- function(input, output, session) {
observeEvent(input$x, {
choices <- seq_len(length(input$x)+1)
names(choices) <- rep("^", length(choices))
updateSelectInput(session, "x", choices = choices, selected = isolate(input$x))
})
}
shinyApp(ui, server)

With multiple choices it gets a bit more complicated
library(shiny)
my_choices <- c('Hello', 'World')
ui <- fluidPage(
selectInput(
inputId = "ManyDuplicated",
label = 'SelectInput',
choices = my_choices,
selected = NULL,
multiple = TRUE
)
)
server <- function(input, output, session) {
observeEvent(input$ManyDuplicated, {
selected_values <- input$ManyDuplicated
names(selected_values) <- gsub("\\..*", "", selected_values)
print( paste( "Current selection :",
paste( names(selected_values), collapse = ", ")))
number_of_items <- length(input$ManyDuplicated)
new_choices <- paste(my_choices, number_of_items + 1, sep = ".")
names(new_choices) <- my_choices
all_choices <- c(selected_values, new_choices )
updateSelectInput(session, "ManyDuplicated",
choices = all_choices,
selected = isolate(input$ManyDuplicated))
})
}
shinyApp(ui = ui, server = server)

shinyApp(
ui = fluidPage(
selectInput("choose", "Choose",
sort(c("a" = "a1", "b" = "b2")),
multiple = TRUE
)
),
server = function(input, output, session) {
old_choose = c()
old_choices = sort(c("a" = "a1", "b" = "b2"))
idx <- 2
observeEvent(input$choose, {
req(!identical(old_choose, input$choose))
addition <- base::setdiff(input$choose, old_choose)
if (length(addition) > 0) {
idx <<- idx + 1
new_nm <- names(old_choices[old_choices == addition])
new_val <- paste0(new_nm, idx)
choices <- c(old_choices, new_val)
names(choices) <- c(names(old_choices), new_nm)
}
missing <- base::setdiff(old_choose, input$choose)
if (length(missing) > 0) {
missing_idx <- which(old_choices == missing)
choices <- old_choices[-missing_idx]
}
choices <- sort(choices)
updateSelectInput(session, "choose",
choices = choices,
selected = input$choose
)
old_choose <<- input$choose
old_choices <<- choices
}, ignoreNULL = FALSE)
}
)

Related

Dynamically update two selectInput boxes based on the others selection in R Shiny module is not working

I have two selectInput boxes in my ShinyApp. Both of them take the same inputs, i.e., the column names of an uploaded table.
I want to make the two input box mutually exclusive, meaning if a column name is selected in one input box, it will become unavailable in the second input box, and vice versa.
Here is my code, and it works.
library(shiny)
ui <- fluidPage(
fileInput(inputId = "rawFile",
label = "Upload Data Table:",
multiple = FALSE,
accept = c(".csv")
),
uiOutput(outputId = "v1",
label = "Select Variable 1"
),
uiOutput(outputId = "v2",
label = "Select Variable 2"
)
)
server <- function(input, output, session){
inputData <- reactive({
inFile <- input$rawFile
if(is.null(inFile)){return(NULL)}
extension <- tools::file_ext(inFile$name)
filepath <- inFile$datapath
df <- read.csv(filepath, header = TRUE)
return(df)
})
output$v1 <- renderUI({
shiny::req(inputData())
selectInput(inputId = "v1",
label = "Select columns to remove",
multiple = TRUE,
choices = names(inputData())
)
})
output$v2 <- renderUI({
shiny::req(inputData())
selectInput(inputId = "v2",
label = "Select columns to remove",
multiple = TRUE,
choices = names(inputData())
)
})
observe({
if(!is.null(input$v2))
updateSelectInput(session, "v1",
choices = names(inputData())[!(names(inputData()) %in% input$v2)],
selected = isolate(input$v1)
)
})
observe({
if(!is.null(input$v1))
updateSelectInput(session, "v2",
choices = names(inputData())[!(names(inputData()) %in% input$v1)],
selected = isolate(input$v2)
)
})
}
shinyApp(ui = ui, server = server)
But when I put this code in a module, it is not working. I don't where the problem is.
library(shiny)
ui_1 <- function(id){
ns <- NS(id)
tagList(
fluidPage(
fileInput(inputId = ns("rawFile"),
label = "Upload Data Table:",
multiple = FALSE,
accept = c(".csv")
),
uiOutput(outputId = ns("v1"),
label = "Select Variable 1"
),
uiOutput(outputId = ns("v2"),
label = "Select Variable 2"
)
)
)
}
server_1 <- function(id){
moduleServer( id, function(input, output, session){
ns <- session$ns
inputData <- reactive({
inFile <- input$rawFile
if(is.null(inFile)){return(NULL)}
extension <- tools::file_ext(inFile$name)
filepath <- inFile$datapath
df <- read.csv(filepath, header = TRUE)
return(df)
})
output$v1 <- renderUI({
shiny::req(inputData())
selectInput(inputId = ns("v1"),
label = "Select columns to remove",
multiple = TRUE,
choices = names(inputData())
)
})
output$v2 <- renderUI({
shiny::req(inputData())
selectInput(inputId = ns("v2"),
label = "Select columns to remove",
multiple = TRUE,
choices = names(inputData())
)
})
observe({
if(!is.null(input$v2))
updateSelectInput(session, ns("v1"),
choices = names(inputData())[!(names(inputData()) %in% input$v2)],
selected = isolate(input$v1)
)
})
observe({
if(!is.null(input$v1))
updateSelectInput(session, ns("v2"),
choices = names(inputData())[!(names(inputData()) %in% input$v1)],
selected = isolate(input$v2)
)
})
}
)
}
The issue is that you wrapped the input id's in ns() inside your updateSelectInputs. You have to do so in renderUI only.
Note: I replaced the code to read a file with mtcars.
library(shiny)
ui_1 <- function(id) {
ns <- NS(id)
tagList(
fluidPage(
fileInput(
inputId = ns("rawFile"),
label = "Upload Data Table:",
multiple = FALSE,
accept = c(".csv")
),
uiOutput(
outputId = ns("v1"),
label = "Select Variable 1"
),
uiOutput(
outputId = ns("v2"),
label = "Select Variable 2"
)
)
)
}
server_1 <- function(id) {
moduleServer(id, function(input, output, session) {
ns <- session$ns
inputData <- reactive({
mtcars
})
output$v1 <- renderUI({
shiny::req(inputData())
selectInput(
inputId = ns("v1"),
label = "Select columns to remove",
multiple = TRUE,
choices = names(inputData())
)
})
output$v2 <- renderUI({
shiny::req(inputData())
selectInput(
inputId = ns("v2"),
label = "Select columns to remove",
multiple = TRUE,
choices = names(inputData())
)
})
observe({
if (!is.null(input$v2)) {
updateSelectInput(session, "v1",
choices = names(inputData())[!(names(inputData()) %in% input$v2)],
selected = isolate(input$v1)
)
}
})
observe({
if (!is.null(input$v1)) {
updateSelectInput(session, "v2",
choices = names(inputData())[!(names(inputData()) %in% input$v1)],
selected = isolate(input$v2)
)
}
})
})
}
ui <- fluidPage(
ui_1("foo")
)
server <- function(input, output, session) {
server_1("foo")
}
shinyApp(ui, server)

How to set up actionButton() or actionBttn() to clear all selections in pickerInput()

When I click on the Action Button, I would like to clear everything: both the output and the selections in the picketInput() (input$engine and input$cylinder in the code below). For consistency if I can do it with shinyWidget's actionBttn, that will be great as well.
library(shiny)
library(shinyWidgets)
df <- mtcars
ui <- fluidPage(
sidebarPanel(
pickerInput("engine", "Select engine:", choices = unique(df$vs),
options = list(
`actions-box` = TRUE),
multiple = TRUE
),
pickerInput("cylinder", "Select cylinder:", choices = unique(df$cyl),
options = list(
`actions-box` = TRUE),
multiple = TRUE
),
actionButton("reset", "Clear Selection"),
),
mainPanel(
textOutput("results")
)
)
server <- function(input, output, session) {
data <- reactiveValues()
observeEvent(input$cylinder, {
tmp <- df
tmp1 <- tmp[tmp$vs %in% input$engine, ]
tmp2 <- tmp1[tmp1$cyl %in% input$cylinder, ]
data$tmp2 <- tmp2
})
output$results <- renderText({
if(is.null(data$tmp2)) return()
print(row.names(data$tmp2))
})
observeEvent(input$reset, {
updatePickerInput(session, "engine", NULL)
updatePickerInput(session, "cylinder", NULL)
data$tmp2 <- NULL
})
}
shinyApp(ui = ui, server = server)
You'll have to respect the order of updatePickerInput's parameters or name them. Your above approach would have updated the label.
Please see ?updatePickerInput and check the following:
library(shiny)
library(shinyWidgets)
library(datasets)
DF <- mtcars
ui <- fluidPage(
sidebarPanel(
pickerInput("engine", "Select engine:", choices = unique(DF$vs),
options = list(
`actions-box` = TRUE),
multiple = TRUE
),
pickerInput("cylinder", "Select cylinder:", choices = unique(DF$cyl),
options = list(
`actions-box` = TRUE),
multiple = TRUE
),
actionBttn("reset", "Clear Selection"),
),
mainPanel(
textOutput("results")
)
)
server <- function(input, output, session) {
data <- reactiveValues()
observeEvent(input$cylinder, {
tmp <- DF
tmp1 <- tmp[tmp$vs %in% input$engine, ]
tmp2 <- tmp1[tmp1$cyl %in% input$cylinder, ]
data$tmp2 <- tmp2
})
output$results <- renderText({
req(data$tmp2)
row.names(data$tmp2)
})
observeEvent(input$reset, {
updatePickerInput(session, inputId = "engine", selected = "")
updatePickerInput(session, inputId = "cylinder", selected = "")
data$tmp2 <- NULL
})
}
shinyApp(ui = ui, server = server)

R Shiny - Automatically adding filters with the names of the columns and select values of each column in the data

I would like to add/remove filters based on column names, i.e., if I select 2 column names, those column names should show numericRangeInput or seletizeInput or any other based on the class. Can it be done with conditionalPanel
Here is what I am trying
library(shiny)
nodes = read.csv("data/nodes.csv", header=T, as.is=T)
ui <- shinyUI(
fluidPage(
actionButton("addNode", "Add Node filter", icon=icon("plus", class=NULL, lib="font-awesome")),
uiOutput("filterPage1")
)
)
server <- function(input, output){
i <- 0
observeEvent(input$addNode, {
i <<- i + 1
output[[paste("filterPage",i,sep="")]] = renderUI({
t4 = class(nodes[,names(nodes)[i]])
print(t4)
list(
fluidPage(
fluidRow(
conditionalPanel(
condition = "t4=='character'",
column(6, selectInput(paste("filteringFactor",i,sep=""), paste0(names(nodes4)[i],':'),
choices=unique(nodes[,names(nodes)[i]]), selected=NULL,
width="100%")),
column(6, actionButton(paste("removeFactor",i,sep=""), "",
icon=icon("times", class = NULL, lib = "font-awesome"),
onclick = paste0("Shiny.onInputChange('remove', ", i, ")"))),
condition = "t4=='numeric'",
column(6, sliderInput(paste("filteringFactor",i,sep=""), paste0(names(nodes4)[i],':'),
choices=unique(nodes4[,names(nodes4)[i]]), selected=NULL,
width="100%")),
column(6, actionButton(paste("removeFactor",i,sep=""), "",
icon=icon("times", class = NULL, lib = "font-awesome"),
onclick = paste0("Shiny.onInputChange('remove', ", i, ")")))
)
)
),
uiOutput(paste("filterPage",i + 1,sep=""))
)
})
})
observeEvent(input$remove, {
i <- input$remove
output[[paste("filterPage",i,sep="")]] <- renderUI({uiOutput(paste("filterPage",i + 1,sep=""))})
})
}
shinyApp(ui, server)
I made an example based on the link I shared to elaborate on my comments (yours isn't reproducible):
library(shiny)
library(shinyWidgets)
library(tools)
library(datasets)
d <- data(package = "datasets")
dataset_is <- sapply(gsub(" .*$", "", d$results[,"Item"]), function(x){is(get(x))[1]})
DFs <- names(dataset_is[dataset_is == "data.frame"])
filterParams <- function(vars){
setNames(lapply(vars, function(x){
list(inputId = x, title = paste0(tools::toTitleCase(x), ":"), placeholder = "...")
}), vars)
}
ui <- fluidPage(
fluidRow(
column(
width = 10, offset = 1,
selectInput("dataset", label = "Select dataset", choices = DFs),
tags$h3("Filter data with selectize group"),
uiOutput("panelProxy"),
DT::dataTableOutput(outputId = "table")
)
)
)
server <- function(input, output, session) {
selected_dataset <- reactive({
DF <- get(input$dataset)
setNames(DF, gsub("\\.", "_", names(DF))) # avoid dots in inputId's (JS special character)
})
vars_r <- reactive({
input$vars
})
res_mod <- callModule(
module = selectizeGroupServer,
id = "my-filters",
data = selected_dataset,
vars = vars_r
)
output$table <- DT::renderDataTable({
req(res_mod())
res_mod()
})
output$panelProxy <- renderUI({
available_vars <- names(selected_dataset())
panel(
checkboxGroupInput(
inputId = "vars",
label = "Variables to use:",
choices = available_vars,
selected = available_vars,
inline = TRUE
),
selectizeGroupUI(
id = "my-filters",
params = filterParams(available_vars)
),
status = "primary"
)
})
}
shinyApp(ui, server)

How to updateRadioButtons for special subdivided input

One objective of my code is to subdivide the first radioButtons input, which
was accomplished copying from: post
The problem is, I want also to be able to update radioButtons, based on a secondary input.
In the following code, or any code the expected is to have a subdivided first input that works unifiedly (currently works in example)
The missing part is update the first input, based on the selection of the second input.
library(shiny)
{
radioSubgroup <- function(inputId, id, label, choices, inline = TRUE, selected) {
values <- paste0(id, "-", choices)
choices <- setNames(values, choices)
rb <- radioButtons(inputId, label, choices, selected = selected, inline = inline)
rb$children
}
radioGroupContainer <- function(inputId, ...) {
class <- "form-group shiny-input-radiogroup shiny-input-container"
div(id = inputId, class = class, ...)
}
ui <- fluidPage(
titlePanel("Example: linked radio buttons"),
sidebarLayout(
sidebarPanel(width=6
,h4("Main input in three rows")
,uiOutput("rgc")
,h4("secondary input")
,radioButtons("secondInput","", 1:2)
),
mainPanel(
fluidRow(
column(4,
strong("Selected input:"), textOutput("selectedInput", inline = TRUE)
)
)
)
)
)
server <- function(input, output, session) {
nucsel <- reactive({
input$secondInput
})
output$rgc <- renderUI({
radioGroupContainer("selectedInput",
fluidRow(column(12,
radioSubgroup("selectedInput", "cars", label = "cars:", choices = 1:6
,selected=nucsel())
,radioSubgroup("selectedInput", "pressure", label = "pressure:", choices = 7:12
,selected=character(0))
,radioSubgroup("selectedInput", "faithful", label = "faithful:", choices = 13:18
,selected=character(0))
)
)
)
})
selectedInput <- reactive({
req(input$selectedInput)
parts <- unlist(strsplit(input$selectedInput, "-"))
list(id = parts[1], value = parts[2])
})
output$selectedInput <- renderText({
selectedInput()$value
})
}
}
shinyApp(ui, server)
The code below updates the first radioButtons based on the selection of the second one
library(shiny)
{
radioSubgroup <- function(inputId, id, label, choices, inline = TRUE, selected) {
values <- paste0(id, "-", choices)
choices <- setNames(values, choices)
rb <- radioButtons(inputId, label, choices, selected = selected, inline = inline)
rb$children
}
updateRadioSubgroup <- function(session, inputId, id, inline, selected, ...) {
value <- paste0(id, "-", selected)
updateRadioButtons(session, inputId, label = NULL, choices = NULL, inline = inline, selected = value)
}
radioGroupContainer <- function(inputId, ...) {
class <- "form-group shiny-input-radiogroup shiny-input-container"
div(id = inputId, class = class, ...)
}
ui <- fluidPage(
titlePanel("Example: linked radio buttons"),
sidebarLayout(
sidebarPanel(width=6
,h4("Main input in three rows")
,uiOutput("rgc")
,h4("secondary input")
,radioButtons("secondInput","", 1:2, selected = character(0))
),
mainPanel(
fluidRow(
column(4,
strong("Selected input:"), textOutput("selectedInput", inline = TRUE)
)
)
)
)
)
server <- function(input, output, session) {
nucsel <- reactive({
input$secondInput
})
output$rgc <- renderUI({
radioGroupContainer("selectedInput",
fluidRow(column(12,
radioSubgroup("selectedInput", "cars", label = "cars:", choices = 1:6
,selected=character(0))
,radioSubgroup("selectedInput", "pressure", label = "pressure:", choices = 7:12
,selected=character(0))
,radioSubgroup("selectedInput", "faithful", label = "faithful:", choices = 13:18
,selected=character(0))
)
)
)
})
observe({
req(input$secondInput)
sel <- input$secondInput
updateRadioSubgroup(session, "selectedInput", "cars", selected = sel, inline = TRUE)
})
selectedInput <- reactive({
req(input$selectedInput)
parts <- unlist(strsplit(input$selectedInput, "-"))
list(id = parts[1], value = parts[2])
})
output$selectedInput <- renderText({
selectedInput()$value
})
}
}
shinyApp(ui, server)

Upload a csv file with actionbutton and display a corrplot

I tried to make a web application with R::shiny but I met a problem with a piece of code. Indeed, I would like to upload a csv file and display a correlogram.
I tried to set up the correlogram with the actionbutton() followed by the updateSelectizeInput()
However an error has been occured :
Error: Unsupported index type: NULL
Anybody have a solution ? thanks
NB - I don't want to use the fileInput widget to upload the csv file ! Only by the actionbutton !
library(shiny)
library(readr)
library(corrplot)
library(DT)
# File used for the example
data(iris)
write.csv(x = iris, file = "iris.csv")
#UI
ui <- shinyUI(
fluidPage(
navbarPage(
id = "navbar",
tabPanel(
title = "UPLOAD",
br(),
actionButton(inputId = "file", label = "ADD A FILE")
)
)
)
)
#SERVER
server <- function(input, output, session) {
path <- reactiveValues(pth = NULL)
file.choose2 <- function(...) {
pathname <- NULL;
tryCatch({
pathname <- file.choose();
}, error = function(ex) {
})
pathname;
}
observeEvent(input$file,{
path$pth <- file.choose2()
})
observeEvent(input$file, {
newvalue <- "B"
updateNavbarPage(session, "navbar", newvalue)
})
data <- reactive({
df <- readr::read_csv(file = path$pth)
return(df)
})
observeEvent(input$file, {
appendTab(
inputId = "navbar",
tabPanel(
value = "B",
title = "Corr",
sidebarLayout(
sidebarPanel(
selectizeInput(
inputId = "select04",
label = "Select features",
choices = NULL,
multiple = TRUE)
),
mainPanel(
plotOutput(
outputId = "corrplot01", height = "650px")
)
)
)
)
}, once = TRUE)
# I suppose there is a problem with this line
observeEvent(input$select04, {
col <- names(data())
col.num <- which(sapply(data(), class) == "numeric")
col <- col[col.num]
updateSelectizeInput(session = session, inputId = "select04", choices = col)
})
output$corrplot01 <- renderPlot({
df <- data()
df1 <- df[,input$select04]
corr <- cor(x = df1, use = "pairwise.complete.obs")
corrplot(corr = corr,
title = "")
})
}
shinyApp(ui, server)
I changed your ui and server a bit, but I think that might solve your problem.
I deleted the observeEvent(input$file, ...{}) from the server and added the ui part in the Ui directly.
I also added 3 req() calls in the data reactive, in the second observeEvent(input$select04, ...{}) which I changed to a normal observe and in the renderPlot call.
library(shiny)
library(readr)
library(corrplot)
library(DT)
# File used for the example
data(iris)
write.csv(x = iris, file = "iris.csv", row.names = F)
#UI
ui <- shinyUI(
fluidPage(
navbarPage(
id = "navbar",
tabPanel(
title = "UPLOAD",
br(),
actionButton(inputId = "file", label = "ADD A FILE"),
tabPanel(
value = "B",
title = "Corr",
sidebarLayout(
sidebarPanel(
selectizeInput(width = "300px",
inputId = "select04",
label = "Select features",
choices = NULL,
multiple = TRUE)
),
mainPanel(
plotOutput(
outputId = "corrplot01", height = "650px")
)
)
)
)
)
)
)
#SERVER
server <- function(input, output, session) {
path <- reactiveValues(pth = NULL)
file.choose2 <- function(...) {
pathname <- NULL;
tryCatch({
pathname <- file.choose();
}, error = function(ex) {
})
pathname;
}
observeEvent(input$file,{
path$pth <- file.choose2()
})
observeEvent(input$file, {
newvalue <- "B"
updateNavbarPage(session, "navbar", newvalue)
})
data <- reactive({
req(path$pth)
df <- readr::read_csv(file = path$pth)
return(df)
})
# I suppose there is a problem with this line
observe({
req(names(data()))
col <- names(data())
col.num <- which(sapply(data(), class) == "numeric")
col <- col[col.num]
updateSelectizeInput(session = session, inputId = "select04", choices = col)
})
output$corrplot01 <- renderPlot({
req(input$select04)
df <- data()
df1 <- df[,input$select04]
corr <- cor(x = df1, use = "pairwise.complete.obs")
corrplot(corr = corr,
title = "")
})
}
shinyApp(ui, server)

Resources