shinyWidgets updatePickerInput() server-side update (similar to updateSelectizeInput function)? - r

Does anyone know of a way to use shinyWidgets::pickerInput() with a large number of choices, but speed up the rendering? (similar to server-side selectize using updateSelectizeInput())
What I'm ultimately trying to do: have a shinyWidgets pickerInput() with a large number of choices (~1M).
The problem: it takes a really long time for the app to load with that many choices.
I know that one workaround is to use the shiny::selectizeInput() UI element, because there is a neat way to do a server-side shiny::updateSelectInput(), which makes both the loading of the initial UI element and the subsequent update with a bunch of options run pretty quickly (as described in this article).
However, I really like the formatting options and flexibility that shinyWidgets::pickerInput() offers.
Examples:
This works and loads quickly, but uses shiny::selectizeInput() instead of shinyWidgets::pickerInput(), which I want to use.
library(shiny)
ui = shiny::fluidPage(
shiny::uiOutput(outputId = "ui_output")
)
server = function(input, output) {
output$ui_output = shiny::renderUI({
shiny::selectizeInput(
inputId = "select",
label = "Select:",
choices = NULL
)
})
shiny::observe({
shiny::updateSelectizeInput(
inputId = "select",
choices = 1:1000000,
selected = 1,
server = TRUE
)
})
}
shiny::shinyApp(ui, server)
When trying to do the same with shinyWidgets::pickerInput(), I tried both loading the choices directly into the UI initially:
server = function(input, output) {
output$ui_output = shiny::renderUI({
shinyWidgets::pickerInput(
inputId = "select",
label = "Select:",
choices = 1:1000000
)
})
}
And also trying to same logic that I used in the first example, which is to initially load the UI object with choices = NULL, and then have a shiny::observe() call that updates the UI element's choices (this works, but is really slow):
server = function(input, output) {
output$ui_output = shiny::renderUI({
shinyWidgets::pickerInput(
inputId = "select",
label = "Select:",
choices = NULL
)
})
shiny::observe({
shinyWidgets::updatePickerInput(
session = getDefaultReactiveDomain(),
inputId = "select",
choices = 1:1000000,
selected = 1
)
})
}

Related

How do I use input from `selectizeInput` to filter a list of options and then `updateSelectizeInput`?

BACKGROUND:
I have a large list of stock symbols, 27,000 rows, that I would like to be choices in a selectizeInput() on a shinyApp. Since the list is large I am using server = T in updateSelectizeInput().
AIM:
I would like the options list to not load/render until a user starts typing a string into selectizeInput(), so that I can return all symbols that start with that letter, to reduce loading all 27,000 rows in the input. I would like input$ticker to be what is observed and then what triggers the filtering code logic. How can i achieve this without using a specific button?
Shown below is
intended output, but with a button to produce the behavior instead of the user being in the text box. This is along the lines of what I would like, but does not automatically start searchign when I type in the box and has bad code smell to me.
current logic, using input$ticker in an observer to trigger selection of df and populate updateSelectize() with new choices, but is failing and the app is evaluating too soon?\
trying to load choices once, using upload button only doesn't work
REPREX:
1.
library(shiny)
tickers <- rep(rownames(mtcars), 850)
ui <- {
renderUI(
shiny::fluidRow(
bs4Dash::box(
title = shiny::selectizeInput(
inputId = "ticker",
label = "Ticker:",
choices = NULL,
selected = "AAPL",
options = list(
placeholder = "e.g AAPL",
create = TRUE,
maxOptions = 50L
)
),
actionButton(
inputId = "update",
label = "UPDATE NOW"
),
id = "tickerBox",
closable = F,
maximizable = F,
width = 12,
height = "250px",
solidHeader = FALSE,
collapsible = F
)
)
)
}
server <- function(input, output, session){
choice <- reactive(
tickers[startsWith(tickers$symbol, input$ticker), ]
)
observeEvent(input$update, {
updateSelectizeInput(
session = session,
label = "Ticker:",
inputId ="ticker",
choices = choice(),
server = TRUE
)
})
}
shiny::shinyApp(ui = ui, server = server)
# REPREX for selectize, glitches and `input$ticker` observer causes loop gltich?
library(shiny)
tickers <- rep(rownames(mtcars), 850)
ui <- {
renderUI(
shiny::fluidRow(
bs4Dash::box(
title = shiny::selectizeInput(
inputId = "ticker",
label = "Ticker:",
choices = NULL,
selected = "AAPL",
options = list(
placeholder = "e.g AAPL",
create = TRUE,
maxOptions = 50L
)
),
actionButton(
inputId = "update",
label = "UPDATE NOW"
),
id = "tickerBox",
closable = F,
maximizable = F,
width = 12,
height = "250px",
solidHeader = FALSE,
collapsible = F
)
)
)
}
server <- function(input, output, session){
# updateSelectizeInput(
# session = session,
# label = "Ticker:",
# inputId ="ticker",
# choices = tickers,
# server = TRUE
# )
observeEvent(input$ticker, {
choices <- tickers[startsWith(tickers$symbol, input$ticker), ]
updateSelectizeInput(
session = session,
label = "Ticker:",
inputId ="ticker",
choices = choices,
server = TRUE
)
})
}
shiny::shinyApp(ui = ui, server = server)
# REPREX for selectize
library(shiny)
tickers <- rep(rownames(mtcars), 850)
ui <- {
renderUI(
shiny::fluidRow(
bs4Dash::box(
title = shiny::selectizeInput(
inputId = "ticker",
label = "Ticker:",
choices = NULL,
selected = "AAPL",
options = list(
placeholder = "e.g AAPL",
create = TRUE,
maxOptions = 50L
)
),
actionButton(
inputId = "update",
label = "UPDATE NOW"
),
id = "tickerBox",
closable = F,
maximizable = F,
width = 12,
height = "250px",
solidHeader = FALSE,
collapsible = F
)
)
)
}
server <- function(input, output, session){
# One call to try and load ticker df
observeEvent(input$update, {
updateSelectizeInput(
session = session,
label = "Ticker:",
inputId ="ticker",
choices = ticker,
server = TRUE
)
})
}
shiny::shinyApp(ui = ui, server = server)
SEE SIMILAR POSTS:
SO POST 1, SO POST 2, SO POST 3
What do you think about something like this?
library(shiny)
tickers <- rep(rownames(mtcars), 850)
ui <- fluidPage(
tags$head(
tags$script(
HTML(
'document.addEventListener("keydown", function(e) {
Shiny.setInputValue("key_pressed", e.key);
})'
)
)
),
fluidRow(
column(2, selectizeInput("select", "Select", choices = "")),
column(1, actionButton("btn", "Search"))
)
)
server <- function(input, output, session) {
observeEvent(input$btn, {
req(input$key_pressed)
updateSelectizeInput(session, "select", choices = tickers[startsWith(tickers, input$key_pressed)], server = TRUE)
})
}
shinyApp(ui, server)
Basically I think it is not possible to just use the words which are putted to the selectInput and we need separate input. I think that selectInput is truthy (isTruthy()) only after some option was chosen (and it can't be "" of course), so we can't use anything which is putted as a word to the selectInput box before some option is actually chosen. I'm not sure, but if I'm right, it is necessary to have separate input for what you want.
However, if we could assume that:
User will use only one letter to get the options to choose
Then we can use "keydown" event (keydown). Now the user doesn't need to put anything to the selectInput box, she/he can just use a key in the keyboards, like C (letter size does matter here, because we are using startsWith()) and then push "Search" button (but of course this letter can still be put to the selectInput box to mimic what you tried to achieve). We could even imagine solution without the button, but I'm afraid in most use-cases it will be not recommended, I mean if user can interact with the app using keyboard not only to choose the options, but also for other purposes, then we would recompute new options everytime user uses key in the keyboard for - well - nothing.
Turns out that selectizeInput doesn't accept a df and must be an atomic vector. When I used tickers[[1]], the issue seemed to be solved, and the list would no longer flash.

How does input from insertUI get stored?

I've created a SelectizeInput() UI using the insertUI() function. Essentially, I have an action button which adds a SelectizeInput() every time it's clicked. The idea is that the user selects columns from their data to put into groups. The creation of the UI works fine. I can also see in the Shiny trace that the selection works fine. However, I'm unsure how to access these variables to use in later plots. This is the code I have:
UI:
actionButton("cr_exp", "Create new biological group")
Server:
observeEvent(input$cr_exp, {
insertUI(
selector = "#cr_exp",
where = "afterEnd",
ui = selectizeInput(inputId = paste0("grp", input$cr_exp), label = "Select samples", choices = colnames(exp_dff()), options = list(create=TRUE), multiple=TRUE))
tags$div(id = paste0("grp", input$cr_exp))
})
In the shiny trace, it shows that the group is created, but I can't figure out how to access the value:
RECV {"method":"update","data":{"grp1":["MV4negControl01","MV4negControl02"]}}
You access the values just like any other input values: by the input element’s
id from the input reactive values:
library(shiny)
ui <- fluidPage(
actionButton("cr_exp", "Create new biological group"),
verbatimTextOutput("choices")
)
server <- function(input, output, session) {
observeEvent(input$cr_exp, {
insertUI(
selector = "#cr_exp",
where = "afterEnd",
ui = selectizeInput(
inputId = paste0("grp", input$cr_exp),
label = "Select samples",
choices = LETTERS,
options = list(create = TRUE),
multiple = TRUE
)
)
tags$div(id = paste0("grp", input$cr_exp))
})
output$choices <- renderPrint({
lapply(seq_len(input$cr_exp), function(i) input[[paste0("grp", i)]])
})
}
shinyApp(ui, server)

selectizeInput paramter 'multiple' unchangeable when updated server-side

I have a selectizeInput (with parameter multiple = FALSE) in a shiny app. I´m not able to change the multiple-parameter afterwards by using the server-side updateSelectizeInput() and setting the option there.
Here is an example:
library(shiny)
ui <- fluidPage(
selectizeInput(
inputId = "name",
label = "Select Name:",
choices = NULL
)
)
server <- function(input, output, session) {
updateSelectizeInput(
inputId = "name",
choices = c("Markus", "Lisa", "Peter"),
options = list(maxItems = 10),
server = TRUE # set consciously, I have a big list to handle
)
}
shinyApp(ui, server)
If I don´t set the server parameter to TRUE, everything works just fine. Is this a bug or do I miss something?
To select multiple items, you can set multiple=TRUE in the selectizeInput as shown below.
library(shiny)
ui <- fluidPage(
selectizeInput(
inputId = "name",
label = "Select Name:",
choices = NULL, multiple=T
)
)
server <- function(input, output, session) {
updateSelectizeInput(
inputId = "name",
choices = c("Markus", "Lisa", "Peter"),
options = list(maxItems = 10),
server = TRUE # set consciously, I have a big list to handle
)
}
shinyApp(ui, server)

Shiny doesn't show me the entire selectInput when I have choices > 1000

I have written a simple example of what I am doing. I have 3000 numbers that I want to show in a selectInput. The numbers have to be in a reactive function, since in my original work, the data is from a file.
My problem is that when I run the app it only appears 1000 numbers, not the entire data (3000 numbers).
I have seen this post Updating selection of server-side selectize input with >1000 choices fails but I don't know how can I do it using uiOutput and renderUI.
Can anyone help me?
Thanks very much in advance
The code:
library(shiny)
ui <- fluidPage(
titlePanel("Numbers"),
sidebarLayout(
sidebarPanel(
uiOutput('selectUI')
),
mainPanel(
)
)
)
server <- function(input, output) {
num <- reactive({
data = c(1:3000)
return(data)
})
output$selectUI <- renderUI({
selectInput(inputId = 'options', "Select one", choices = num())
})
}
# Run the application
shinyApp(ui = ui, server = server)
Use selectizeInput instead of selectInput with the argument options = list(maxOptions = 3000).
Thanks to Stéphane Laurent's answer, the example will be solved like this:
library(shiny)
ui <- fluidPage(
titlePanel("Numbers"),
sidebarLayout(
sidebarPanel(
selectizeInput(inputId = "options", label = "Select one", choices=character(0)),
),
mainPanel(
)
)
)
server <- function(input, output, session) {
num <- reactive({
data = c(1:3000)
return(data)
})
observe({
updateSelectizeInput(
session = session,
inputId = "options",
label = "Select one",
choices= num(), options=list(maxOptions = length(num())),
server = TRUE)
})
}
# Run the application
shinyApp(ui = ui, server = server)
This code will work if you have more than 3000 entries. It will show you ALL the choices that you have. However, if you have a long list of choices (e.g. 60000) it will decrease the speed of your app.

Update the choices argument of the same radioButtons in shiny

I want to update the choices-argument from a radioButtons-widget in R/shiny. When a user selected a choice, the choices argument should be updated based on the first choice of the user. I mocked this with 4 random letters with the sample-function. It appears that the update does not stop and is updated several times. How do I prevent the behavior of updating multiple times?
Here is code to reproduce my approach:
library("shiny")
ui <- fluidPage(
radioButtons("answerchoice", label = "item", choices = sample(letters, 4), selected = NULL,
)
)
server <- function(input, output, session) {
observeEvent(input$answerchoice,{
updateRadioButtons(
session = session,
inputId = "answerchoice",
choices = sample(letters, 4)
)
})
}
shinyApp(ui = ui, server = server)
Seems like the default setting of selected = NULL might be the problem. radioButton is picking one value initally. This may cause the multiple updates. By setting selected to nothing. The app is not updating uncontrollably.
library("shiny")
ui <- fluidPage(
radioButtons("answerchoice",
label = "item",
choices = sample(letters, 4),
selected = character(0)
)
)
server <- function(input, output, session) {
observeEvent(input$answerchoice,{
updateRadioButtons(
session = session,
inputId = "answerchoice",
choices = sample(letters, 4),
selected = character(0)
)
})
}
shinyApp(ui = ui, server = server)

Resources