I am trying to come up with a way to prevent a select input from resetting when the data it depends upon changes. Ideally, as more data arrives, the choices expand, silently, without visual disruption or input value resetting. I've tried using updateSelectInput, but without success. I've created an example that reasonably approximates my problem, have left in my comments and ideas to show where I tried to come up with a solution, and am hoping someone else has a better idea they can share. As always, thank you in advance. -nate
library(shiny)
if (interactive()) {
ui <- fluidPage(
titlePanel("Is It Possible To Prevent The Select Input From Resetting with New Data Arriving?"),
sidebarLayout(
sidebarPanel(
shiny::uiOutput(outputId = "streaming_select")
),
mainPanel(
tableOutput("table")
)
)
)
server<- function(input, output, session){
session_launched<- reactiveValues(count=1)
fake_global_rv_list<- reactiveValues()
fake_global_rv_list$tmp<- data.frame(glob_0001=runif(10))
session_rv_list<- reactiveValues()
session_rv_list$tmp<- data.frame(sess_0001=runif(10))
# Simulating Streaming Data every 7 seconds
shiny::observe({
shiny::invalidateLater(millis = 7000)
shiny::isolate({
shiny::showNotification(ui = "Generating Random Data", type = "default", duration = 3)
tmp<- data.frame(runif(10) )
colnames(tmp)<- paste0("stream_",format(as.numeric(Sys.time())))
session_rv_list$tmp<- cbind(session_rv_list$tmp, tmp) # Put the random data into the reactive Values list
})
})
full_dat<- shiny::reactive({ cbind(fake_global_rv_list$tmp, session_rv_list$tmp) })
# Table of 'Streaming' Data
output$table <- renderTable({
full_dat()
})
## Select Input that let's you pick a single column
output$streaming_select<- shiny::renderUI({
if(!is.null(full_dat())){
if(session_launched$count==1){
out<- shiny::selectizeInput(inputId = "streaming_select_input", label="Pick A Column", choices = unique(colnames(full_dat())), selected = NULL, multiple = TRUE)
}
}
})
## Possible Ideas (?) BELOW
# select_choices<- shiny::eventReactive(full_dat(), {
# if(!is.null(full_dat())){
# if(session_launched$count==1){
# out<- list( choices = unique(colnames(full_dat())), selected = NULL)
# #shiny::selectizeInput(inputId = "streaming_select_input", label="Pick A Column", choices = unique(colnames(full_dat())), selected = NULL, multiple = TRUE)
# session_launched$count<- 2
# return(out)
# } else if(session_launched$count > 1){
# old_selections<- input$streaming_select_input
# out<- list( choices = unique(colnames(full_dat())), selected = old_selections)
# return(out)
# #shiny::updateSelectizeInput(session, inputId = "streaming_select_input", choices = unique(colnames(full_dat())), selected = old_selections)
# }
# }
# })
# observeEvent(select_choices(), {
# cat("STR of select_choices is...", "\n")
# cat(str(select_choices()), "\n")
# })
#
# shiny::observeEvent(full_dat(), {
# if(session_launched$count != 1){
# old_selections<- input$streaming_select_input
# shiny::updateSelectizeInput(session, inputId = "streaming_select_input", choices = unique(colnames(full_dat())), selected = old_selections)
# }
# })
}
shinyApp(ui, server)
}
Below is an example that works. I create the selectizeInput in the ui part, and update it on change of the full_dat data frame using an observeEvent. I had to store and reset the selection in this update step to prevent it from being set to NULL.
library(shiny)
if (interactive()) {
ui <- fluidPage(
titlePanel("Is It Possible To Prevent The Select Input From Resetting with New Data Arriving?"),
sidebarLayout(
sidebarPanel(
shiny::selectizeInput(inputId = "streaming_select_input", label="Pick A Column",
choices = NULL,
selected = NULL,
multiple = TRUE)
),
mainPanel(
tableOutput("table")
)
)
)
server<- function(input, output, session){
session_launched<- reactiveValues(count=1)
fake_global_rv_list<- reactiveValues()
fake_global_rv_list$tmp<- data.frame(glob_0001=runif(10))
session_rv_list<- reactiveValues()
session_rv_list$tmp<- data.frame(sess_0001=runif(10))
# Simulating Streaming Data every 7 seconds
shiny::observe({
shiny::invalidateLater(millis = 7000)
shiny::isolate({
shiny::showNotification(ui = "Generating Random Data", type = "default", duration = 3)
tmp<- data.frame(runif(10) )
colnames(tmp)<- paste0("stream_",format(as.numeric(Sys.time())))
session_rv_list$tmp<- cbind(session_rv_list$tmp, tmp) # Put the random data into the reactive Values list
})
})
full_dat<- shiny::reactive({ cbind(fake_global_rv_list$tmp, session_rv_list$tmp) })
# Table of 'Streaming' Data
output$table <- renderTable({
full_dat()
})
## Select Input that let's you pick a single column
observeEvent(full_dat(), {
selectedCols <- input$streaming_select_input
updateSelectizeInput(session, "streaming_select_input", choices = colnames(full_dat()), selected = selectedCols)
})
}
shinyApp(ui, server)
}
Related
Unable to reset the textinput and selectinput. I tried to create the action button reset. Also used observeEvent. could you please help. I want to understand why the reset with observeEvent is not working, also when I manually clear the textinput, the app gives error. Any reason
libraries:
library(shiny)
library(shinyjs)
library(magrittr)
library(tidyverse)
UI part
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Package with datasets and functions"),
div(id='form',
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
textInput('pkg','Package Name', value = NULL),
actionButton("update", "Update View"),
actionButton("reset", "Reset inputs"),
helpText('Please enter the package name for which you want to see the list of datasets and functions (with parameters)'),
br(),
# br(),
selectInput('dat','Datasets', choices = NULL, selected = NULL)
),
# Show a plot of the generated distribution
mainPanel(
tabsetPanel(
id = 'dataset',
tabPanel("List of Datasets in the Package", DTOutput("dataset1")),
tabPanel("Datasets View", DTOutput("dataset2")),
tabPanel("List of Functions with Parameters in the Package", verbatimTextOutput('func'))
)
)
)
)
)
server part
# Define server logic required to draw a histogram
server <- function(input, output, session) {
pkgs <- reactive({input$pkg})
observeEvent(input$reset, {
#pkgs() <- NULL
updateSelectInput(session, 'dat','Datasets', choices = NULL, selected = NULL)
updateTextInput('pkg','Package Name', value = NULL)
})
#
# if (!is.null(pkgs())){
df <- reactive({
# pksis <- require(input$pkg)
# cat(pksis)
# if (input$pkg %in% rownames(installed.packages()) == TRUE) {
data_name1 <- data(package=input$pkg)
data_name2 <- as_tibble(data_name1$results) %>% rename(name=Item, label=Title) %>% select(-LibPath, -Package)
data_name2
# } else {
# install.packages(input$pkg)
# library(input$pkg)
# data_name1 <- data(package=input$pkg)
# data_name2 <- as_tibble(data_name1$results) %>% rename(name=Item, label=Title) %>% select(-LibPath, -Package)
# data_name2
# }
})
# }
obse <- eventReactive(input$update, { df() })
funct <- eventReactive(input$update, { paste0('package:',input$pkg) })
# if (!is.null(pkg1())){
observe({
req(obse())
updateSelectInput(session, inputId = "dat", label = "Datasets", choices = c(df()$name), selected = df()$name[1])
})
# }
df2 <- reactive({
req(obse())
e <- new.env()
library(package = input$pkg, character.only = TRUE)
out <- data(list=input$dat, package = input$pkg, envir = e)
e[[out]]
# new <- input$dat
# data(new, package = input$pkg)
# cat(new)
})
output$dataset1 <- renderDataTable({
DT::datatable(obse())
})
output$dataset2 <- renderDataTable({
df2()
})
output$func <- renderPrint({
lsf.str(funct())
})
observeEvent(input$reset,{
output$dataset1 <- renderDataTable({
})
output$dataset2 <- renderDataTable({
})
output$func <- renderPrint({
})
})
}
# Run the application
# undebug(shinyApp)
shinyApp(ui = ui, server = server)
The following code makes the order of update and reset clearer in the server part.
library(shiny)
library(shinyjs)
library(magrittr)
library(tidyverse)
require(DT)
ui <- fluidPage(
# Application title
titlePanel("Package with datasets and functions"),
div(id='form',
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
textInput('pkg','Package Name', value = NULL),
actionButton("update", "Update View"),
actionButton("reset", "Reset inputs"),
helpText('Please enter the package name for which you want to see the list of datasets and functions (with parameters)'),
br(),
# br(),
selectInput('dat','Datasets', choices = NULL, selected = NULL)
),
# Show a plot of the generated distribution
mainPanel(
tabsetPanel(
id = 'dataset',
tabPanel("List of Datasets in the Package", DTOutput("dataset1")),
tabPanel("Datasets View", DTOutput("dataset2")),
tabPanel("List of Functions with Parameters in the Package", verbatimTextOutput('func'))
)
)
)
)
)
server <- function(input, output, session) {
pkgs <- reactive({
req(input$pkg)
})
##### update
observeEvent(input$update, {
updateTextInput(inputId = 'pkg', value = pkgs())
# check if this_package is installed
if(system.file(package = pkgs()) == ""){
updateSelectInput(session, 'dat', choices = NULL , selected = NULL)
}
else{
# data sets in the package
data_pkg <- data(package = pkgs())
# names of data sets in the package
data_names <- data_pkg$results[, "Item"]
updateSelectInput(session, 'dat', choices = data_names, selected = data_names[1])
### dataset1 - data names
output$dataset1 <- renderDataTable({
DT::datatable(data.frame(data_names))
})
### dataset3 - function list
funs <- paste0('package:', pkgs())
output$func <- renderPrint({
lsf.str(funs)
})
}
})
### dataset2 - selected dataset
data_name <- reactive({
req(input$dat)
})
output$dataset2 <- renderDataTable({
e <- new.env()
library(package = pkgs(), character.only = TRUE)
out <- data(list=data_name(), package = pkgs(), envir = e)
d2 <- e[[out]]
# some datasets are 3-d, e.g., "ozone" in package "plyr"
if(length(dim(d2)) == 3){
d2 <- d2[, , 1]
}
DT::datatable(d2)
})
##### reset
observeEvent(input$reset, {
updateTextInput(inputId = 'pkg',value = NULL)
updateSelectInput(session, 'dat', choices = NULL , selected = NULL)
output$dataset1 <- renderDataTable({
DT::datatable(data.frame("No package selected" = NULL))
})
})
}
# Run the application
shinyApp(ui = ui, server = server)
I am a beginer of shiny, and I am building a shiny app using win10 system, rstudio, and shiny version 1.7.1. I would like to make it more user oriented. It means that other parts of the application will be hid unless user uploads correct data. After many attempts, I decided to use session$userData and shinyjs::toggle to develop this app. But I am confused by session$userData. In the beginning, by reading the official documentation, I think it just like the global environment of r. But obviously not. So I just want to know how to use it correctly, or how to realize the features I want. There are three examples I had tried, they are for your reference.
Please note that the third example is almost what I want, but I don't think it's elegant since the continue button is somewhat redundant.
Examples 1:
I would like to check whether there is data input or whether the input data is a csv format, if true, show the data, and if not, the rest part of the app will be hid. In this case you can see, although you data have passed the check, the tablepanel b will still show nothing, unless before input data you have clicked tablepanel b, or unless after data checking you clicked button go again.
##### 1. packages #####
library(shiny)
library(shinyjs)
##### 2. ui #####
ui <- fluidPage(
useShinyjs(),
tabsetPanel(
tabPanel("a",
sidebarLayout(
sidebarPanel(uiOutput("ui_p1_sidebar1"), uiOutput("ui_p1_sidebar2")),
mainPanel(uiOutput("ui_p1_main"))
)),
tabPanel("b",
sidebarLayout(
sidebarPanel(uiOutput("ui_p2_sidebar")),
mainPanel(uiOutput("ui_p2_main"))
))
)
)
##### 3. server #####
server <- function(input, output, session) {
output$ui_p1_sidebar1 <- renderUI({
fileInput(inputId = "p1s_inputdata",
label = "Input data",
multiple = FALSE,
accept = ".csv")
})
output$ui_p1_sidebar2 <- renderUI({
shiny::actionButton(inputId = "p1s_go",
label = "go",
icon = icon("play"))
})
observeEvent(input$p1s_go,{
isolate({
data <- input$p1s_inputdata
})
output$ui_p1_main <- renderUI({
tagList(
h3("Data check: "),
verbatimTextOutput(outputId = "p1m_datacheck", placeholder = T),
h3("Data show: "),
verbatimTextOutput(outputId = "p1m_datashow", placeholder = T),
)
})
output$p1m_datacheck <- renderPrint({
# data check part, the result of checking is stored by session$userData$sig
if(is.null(data)){
cat("There is no data input! \n")
session$userData$sig <- F
} else{
dataExt <- tools::file_ext(data$name)
if(dataExt != "csv"){
cat("Please input csv data! \n")
session$userData$sig <- F
} else{
cat("Data have passed the check!")
session$userData$data <- read.csv(data$datapath)
session$userData$sig <- T
}
}
})
output$p1m_datashow <- renderPrint({
if(session$userData$sig){
print(session$userData$data)
} else{
cat("Please check the data!")
}
})
output$ui_p2_sidebar <- renderUI({
radioButtons("aaa", "aaa", choices = c("a", "b", "c"))
})
output$ui_p2_main <- renderUI({
verbatimTextOutput(outputId = "p2m_print", placeholder = T)
})
output$p2m_print <- renderPrint({print(letters[1:10])})
observe({
toggle(id = "ui_p2_sidebar", condition = session$userData$sig)
toggle(id = "ui_p2_main", condition = session$userData$sig)
})
})
}
##### 4. app #####
shinyApp(ui = ui, server = server)
Example 2:
In this small case you can see, in a samle module, session$userData$... changed timely, but in another module, it will not change unless you click the button again. It that means session$userData$... could have different values at the same time?
##### 1. packages #####
library(shiny)
##### 2. ui #####
ui <- fluidPage(
sidebarLayout(
sidebarPanel(uiOutput("ui_sidebar")),
mainPanel(uiOutput("ui_main1"), uiOutput("ui_main2"))
)
)
##### 3. server #####
server <- function(input, output, session) {
output$ui_sidebar <- renderUI({
tagList(
radioButtons("s_letter", "letters", choices = c("a", "b", "c")),
shiny::actionButton(inputId = "go1",
label = "GO1",
icon = icon("play"))
)
})
observeEvent(input$go1, {
output$ui_main1 <- renderUI({
tagList(
h3("module 1: shared value changes timely."),
verbatimTextOutput(outputId = "m1", placeholder = T),
h3("module 2: shared value changes by button."),
verbatimTextOutput(outputId = "m2", placeholder = T)
)
})
output$m1 <- renderPrint({
out <- switch (input$s_letter,
"a" = "choose a",
"b" = "choose b",
"c" = "choose c")
session$userData$sharedout <- out
cat("out: \n")
print(out)
cat("sharedout: \n")
print(session$userData$sharedout)
})
output$m2 <- renderPrint({
cat("sharedout: \n")
print(session$userData$sharedout)
})
})
}
##### 4. app #####
shinyApp(ui = ui, server = server)
Example 3: I also tried other solutions. There is a modification of example 1, I have added a continue button to realize my thought. It works well, but I hope the hidden action is based on conditions rather than events. So how to remove the button and let the rest part displayed automatically if data passed checking?
##### 1. packages #####
library(shiny)
##### 2. ui #####
ui <- fluidPage(
tabsetPanel(
tabPanel("a",
sidebarLayout(
sidebarPanel(uiOutput("ui_p1_sidebar1"), uiOutput("ui_p1_sidebar2")),
mainPanel(uiOutput("ui_p1_main"))
)),
tabPanel("b",
sidebarLayout(
sidebarPanel(uiOutput("ui_p2_sidebar")),
mainPanel(uiOutput("ui_p2_main"))
))
)
)
##### 3. server #####
server <- function(input, output, session) {
output$ui_p1_sidebar1 <- renderUI({
fileInput(inputId = "p1s_inputdata",
label = "Input data",
multiple = FALSE,
accept = ".csv")
})
output$ui_p1_sidebar2 <- renderUI({
shiny::actionButton(inputId = "p1s_go",
label = "go",
icon = icon("play"))
})
observeEvent(input$p1s_go,{
isolate({
data <- input$p1s_inputdata
})
output$ui_p1_main <- renderUI({
tagList(
h3("Data check: "),
verbatimTextOutput(outputId = "p1m_datacheck", placeholder = T),
uiOutput("ispass"),
h3("Data show: "),
verbatimTextOutput(outputId = "p1m_datashow", placeholder = T)
)
})
output$p1m_datacheck <- renderPrint({
if(is.null(data)){
cat("There is no data input! \n")
session$userData$sig <- F
} else{
dataExt <- tools::file_ext(data$name)
if(dataExt != "csv"){
cat("Please input csv data! \n")
session$userData$sig <- F
} else{
cat("Data have passed the check!")
session$userData$data <- read.csv(data$datapath)
session$userData$sig <- T
}
}
})
output$ispass <- renderUI({
if(isFALSE(session$userData$sig)){
return()
} else{
shiny::actionButton(inputId = "ispass",
label = "continue",
icon = icon("play"))
}
})
})
observeEvent(input$ispass,{
output$p1m_datashow <- renderPrint({
if(session$userData$sig){
print(session$userData$data)
} else{
cat("Please check the data!")
}
})
output$ui_p2_sidebar <- renderUI({
radioButtons("aaa", "aaa", choices = c("a", "b", "c"))
})
output$ui_p2_main <- renderUI({
verbatimTextOutput(outputId = "p2m_print", placeholder = T)
})
output$p2m_print <- renderPrint({print(letters[1:10])})
})
}
##### 4. app #####
shinyApp(ui = ui, server = server)
I hope the following refactoring will help and does what you want.
An essential tool for hiding,showing and updating UI elements can be the renderUI, but often this is overkill because of rerenderings.
But I would suggest using the shinyjs-package which gives you functions like shinyjs::show and shinyjs::hide for showing and hiding. For updating UI-elements, there are functions like shiny::updateActionButton,shiny::updateCheckboxInput, shiny::updateRadioButtons, ....
It is (always) useful to give your UI-elements IDs, like the tabsetPanel.
Moreover, a nice tool too is shiny::conditionalPanel, but you will dive into all this stuff when programming more apps. :)
##### 1. packages #####
library(shiny)
myapp <- function() {
##### 2. ui #####
ui <- fluidPage(
tabsetPanel(
tabPanel("a",
sidebarLayout(
sidebarPanel(
fileInput(inputId = "p1s_inputdata", label = "Input data", multiple = FALSE, accept = ".csv")
),
mainPanel(uiOutput("ui_p1_main"))
)),
tabPanel("b",
sidebarLayout(
sidebarPanel(radioButtons("aaa", "aaa", choices = c("some", "placeholder", "stuff"))),
mainPanel(verbatimTextOutput(outputId = "p2m_print", placeholder = T))
)),
id = "TABSETPANEL"
)
)
##### 3. server #####
server <- function(input, output, session) {
shiny::hideTab(inputId = "TABSETPANEL", target = "b", session = session)
observeEvent(input$p1s_inputdata, {
data <- input$p1s_inputdata
dataCheckText <- NULL
if(is.null(data)){
dataCheckText <- "There is no data input!"
session$userData$sig <- F
} else{
dataExt <- tools::file_ext(data$name)
if(dataExt != "csv"){
dataCheckText <- "Please input csv data!"
session$userData$sig <- F
} else{
dataCheckText <- "Data have passed the check!"
session$userData$data <- read.csv(data$datapath)
session$userData$sig <- T
}
}
output$p1m_datacheck <- renderPrint(dataCheckText)
if(session$userData$sig) shiny::showTab(inputId = "TABSETPANEL", target = "b", session = session)
else shiny::hideTab(inputId = "TABSETPANEL", target = "b", session = session)
main1Taglist <- tagList(
h3("Data check: "),
verbatimTextOutput(outputId = "p1m_datacheck", placeholder = T)
)
if(session$userData$sig) {
shiny::showTab(inputId = "TABSETPANEL", target = "b", session = session)
output$p1m_datashow <- renderPrint({
print(session$userData$data)
})
main1Taglist <- c(main1Taglist, tagList(
h3("Data show: "),
verbatimTextOutput(outputId = "p1m_datashow", placeholder = T)
))
#Update stuff in panel b according to the new data
updateRadioButtons(session = session, inputId = "aaa", choices = names(session$userData$data))
output$p2m_print <- renderPrint({print(letters[1:10])})
}
output$ui_p1_main <- renderUI(main1Taglist)
})
}
##### 4. app #####
shinyApp(ui = ui, server = server)
}
myapp()
You're somewhat on the right track. Try something like this:
observeEvent(input$go1, {
# Perform data validation here.
# This would look similar to what you have inside output$p1m_datacheck <- renderPrint({})
# If data file is no good, do nothing, exit this function: return()
# Else, data file is good, continue
# Do your output$* <- render*() functions here
})
You don't need to isolate() inside the handlerExpr of observeEvent(). It will already be executed in an isolate() scope.
I am trying to create my first shiny app but I am facing a difficulty: in the reproducible example below I am creating a reactive pickerInput (i.e. only show brands proposing a cylindre equal to the input visitors select).
I then want that based on the combination input_cyl and picker_cny (remember that picker_cny depends on input_cyl) to display a table which shows the relevant data for the observation matching the combination input_cyl and picker_cny.
Thank you for your help!
df <- mtcars
df$brand <- rownames(mtcars)
df$brand <- gsub("([A-Za-z]+).*", "\\1", df$brand)
if (interactive()) {
library(shiny)
library(shinyWidgets)
library(shinythemes)
library(shinycssloaders)
# Define UI -----------------------------------------------
ui <- fluidPage(
# Application title
titlePanel("Reproducible Example"),
# Parameters
sidebarLayout(
sidebarPanel(
selectInput(inputId = "input_cyl", label = "Cyl",
choices = c("6", "4", "8")),
pickerInput(
inputId = "picker_cny",
label = "Select Company",
choices = paste0(unique(df$brand)),
options = list(`actions-box` = TRUE),
multiple = TRUE),
width = 2),
# Show Text
mainPanel(
tableOutput("table"),
width = 10)
))
# Define Server ------------------------------------------
server <- function(input, output, session) {
# Reactive pickerInput ---------------------------------
observeEvent(input$input_cyl, {
df_mod <- df[df$cyl == paste0(input$input_cyl), ]
# Method 1
disabled_choices <- !df$cyl %in% df_mod$cyl
updatePickerInput(session = session,
inputId = "picker_cny",
choices = paste0(unique(df$brand)),
choicesOpt = list(
disabled = disabled_choices,
style = ifelse(disabled_choices,
yes = "color: rgba(119, 119, 119, 0.5);",
no = "")
))
}, ignoreInit = TRUE)
output$table <- renderTable(df)
}
}
# Run the application
shinyApp(ui = ui, server = server)
You need a reactive that will handle the change in the input and subset the dataframe before giving it to the output table. For that, you just need to add this block to your server:
data <- reactive({
if (length(input$picker_cny) > 0)
df[df$brand %in% input$picker_cny,]
else
df
})
and update the output$table like this:
output$table <- renderTable(data())
Note: feel free to remove the if else in the reactive to get that:
data <- reactive({
df[df$brand %in% input$picker_cny,]
})
The only difference in that case is: would you show all or nothing when no input has been entered yet. That's a matter of taste.
I have almosat completed a very basic web app in shiny.
I have it functioning as intended, however I believe I have incorrectly used "updatePickerInput" as the table is rendered as expected, however I am not able to select any options in my two pickers as it seems to continue to instantly refresh. I assume this is because the session is looking for input and then regenerating the output, which includes my picker refresh (so I have causes a cyclical refresh). I may be wrong though.
I have looked up the literature but I am unsure exactly what i have done wrong and what the syntax should be to prevent this from occuring.
Typical input is a .csv matrix with different animals on X-axis row 1 (column names) and Y-axis column 1 (row names) with values between any two animals.
library(shiny)
library(ggplot2)
library(shinyWidgets)
library(DT)
options(shiny.maxRequestSize = 50*1024^2)
# Define UI
ui <- fluidPage(
# Application title
titlePanel("Interactive Relatedness Comparison"),
# Sidebar inputs
sidebarLayout(
mainPanel(dataTableOutput("contents")),
sidebarPanel(
#Upload GRM file
fileInput("file1", "Choose GRM File", accept= c(
"text/csv",
"text/comma-separated-values,text/plain",
".csv")),
#Client can choose sires along x-axis
pickerInput(
inputId = "sireselect",
label = "Select Sires",
choices = "Please Upload GRM",
multiple = TRUE,
options = pickerOptions(actionsBox = TRUE, liveSearch = TRUE),
),
#Client can choose dams along y-axis
pickerInput(
inputId = "damselect",
label = "Select Dams",
choices = "Please Upload GRM",
multiple = TRUE,
options = pickerOptions(actionsBox = TRUE, liveSearch = TRUE),
),
#Show raw values
checkboxInput("relatedness", "Show Values (will reset sorting)", value = FALSE),
)
),
)
server <- function(input, output, session) {
#Output uploaded table as data table
output$contents <- DT::renderDataTable({
rownames = TRUE
inFile <- input$file1
if (is.null(inFile))
return(NULL)
file2 <- read.csv(inFile$datapath)
#shiney data table render was not showing row names correctly, changed to DT
rownames(file2) <- file2[,1]
#Remove first column which is now the rownames
file2 <- file2[-c(1)]
#Update pickers for the row/column names
updatePickerInput(session, inputId = "damselect", choices = rownames(file2), selected = rownames(file2))
updatePickerInput(session, inputId = "sireselect", choices = colnames(file2), selected = colnames(file2))
#Create summarized data table (to be primary view unless raw values selected)
newgrid <- as.data.frame(file2)
#Generate summarised data table
for (irow in 1:nrow(file2)){
for (icol in 1:ncol(file2)){
dig <- file2[irow,icol]
if (dig >= 0.8) {
newgrid[irow,icol] <- "SAME"
} else if (dig >= 0.3) {
newgrid[irow,icol] <- "HIGH"
} else if (dig >= 0.1) {
newgrid[irow,icol] <- "MED"
} else {
newgrid[irow,icol] <- "NOT"
}
}
}
#Check box for raw values or not
if (input$relatedness == TRUE){
return(file2[input$damselect,input$sireselect])
} else {
return(newgrid[input$damselect,input$sireselect])
}
})
}
# Run the application
shinyApp(ui, server)
Any help would ne much appreciated
Read in data and updatePickerInput outside of output$contents might help. Try this
library(shiny)
library(ggplot2)
library(shinyWidgets)
library(DT)
options(shiny.maxRequestSize = 50*1024^2)
# Define UI
ui <- fluidPage(
# Application title
titlePanel("Interactive Relatedness Comparison"),
# Sidebar inputs
sidebarLayout(
mainPanel(DTOutput("contents")),
sidebarPanel(
#Upload GRM file
fileInput("file1", "Choose GRM File", accept= c(
"text/csv",
"text/comma-separated-values,text/plain",
".csv")),
#Client can choose sires along x-axis
pickerInput(
inputId = "sireselect",
label = "Select Sires",
choices = "Please Upload GRM",
multiple = TRUE,
options = pickerOptions(actionsBox = TRUE, liveSearch = TRUE),
),
#Client can choose dams along y-axis
pickerInput(
inputId = "damselect",
label = "Select Dams",
choices = "Please Upload GRM",
multiple = TRUE,
options = pickerOptions(actionsBox = TRUE, liveSearch = TRUE),
),
#Show raw values
checkboxInput("relatedness", "Show Values (will reset sorting)", value = FALSE),
)
)
)
server <- function(input, output, session) {
file3 <- reactive({
rownames = TRUE
inFile <- input$file1
if (is.null(inFile))
return(NULL)
file2 <- read.csv(inFile$datapath)
#shiney data table render was not showing row names correctly, changed to DT
rownames(file2) <- file2[,1]
#Remove first column which is now the rownames
file2 <- file2[-c(1)]
file2
})
observe({
req(file3())
updatePickerInput(session, inputId = "damselect", choices = rownames(file3()), selected = rownames(file3()))
updatePickerInput(session, inputId = "sireselect", choices = colnames(file3()), selected = colnames(file3()))
})
#Output uploaded table as data table
output$contents <- renderDT({
req(file3())
#Create summarized data table (to be primary view unless raw values selected)
newgrid <- as.data.frame(file3())
#Generate summarised data table
for (irow in 1:nrow(file3())){
for (icol in 1:ncol(file3())){
dig <- file3()[irow,icol]
if (dig >= 0.8) {
newgrid[irow,icol] <- "SAME"
} else if (dig >= 0.3) {
newgrid[irow,icol] <- "HIGH"
} else if (dig >= 0.1) {
newgrid[irow,icol] <- "MED"
} else {
newgrid[irow,icol] <- "NOT"
}
}
}
#Check box for raw values or not
if (input$relatedness == TRUE){
return(file3()[input$damselect,input$sireselect])
}else {
return(newgrid[input$damselect,input$sireselect])
}
})
}
# Run the application
shinyApp(ui, server)
I am trying to create a shiny code that is able to filter a table non pre-determined number of times. When the user uploads a different (new) table, unfortunately the code breaks as I need to restart a lapply loop somehow, throwing out the previously stored column names.
I would like to create an non pre-defined filtering options for a table within Shiny. The user can select a column and filter a table choosing different categorical variables within that column. It is possible to add additional selection fields by pressing the 'Add' button.
the UI:
library(shiny)
library(shinydashboard)
library(dplyr)
ui <- shinyUI(
pageWithSidebar(
headerPanel("testing of dynamic number of selection"),
sidebarPanel(
uiOutput("buttons")),
mainPanel(
uiOutput("drops")
,tableOutput("table")
)
))
The server:
A table (test.csv) is automatically stored in a reactive values and a first searching field appears with 3 buttons (Add = to add a new searching field by reading in the colnames and a multiselect that stores the unique variables from that columns. The filtering function is activated by the Calculate button)
server<-function(input, output, session) {
###### read in test file
values<-reactiveValues(number = 1,
upload = NULL,
input = NULL)
values$upload<-read.csv("test.csv")
#just the "add" button, in this instance it shouldn't be a uiOutput
output$buttons <- renderUI({
div(
actionButton(inputId = "add", label = "Add"), actionButton(inputId = "calc", label = "Calculate"),
actionButton(inputId = "new", label = "new table")
)
})
#pressing the add button
observeEvent(input$add, {
cat("i adding a new record\n")
values$number <- values$number + 1L })
daStuff <- function(i){
inputName<-paste0("drop", i)
inputName2<-paste0("select", i)
inputText<-if(values$number>0){input[[paste0("drop",i)]]}else{F} # previously selected value for dropdown
inputSelect <- if(values$number>1){input[[paste0("select",i)]]}else{F} # previously selected value for dropdown
fluidRow(
column(6,selectInput(inputName, inputName, c(colnames(values$upload)), selected = inputText)),
column(6,selectInput(inputName2, inputName2,
na.omit(unique(as.vector(values$upload[,input[[paste0("drop",i)]]]))),
multiple=TRUE, selectize=TRUE, selected=inputSelect)) )}
output$drops<- renderUI({
lapply(seq_len(values$number), daStuff)})
By pressing the Calculate button, the uploaded table is subjected to filtering, depending on the selected unique values and shown in the output$table
observeEvent(input$calc, {
values$input<-NULL
for (i in 1:values$number){
if(!is.null(input[[paste0("select",i)]])){
if(is.null(values$input)){
values$input<- filter(values$upload,values$upload[,input[[paste0("drop",i)]]] %in% input[[paste0("select",i)]])}
else{
values$input<- filter(values$input,values$input[,input[[paste0("drop",i)]]] %in% input[[paste0("select",i)]])}
} }
if (is.null(values$input)){values$input<-values$upload}
output$table <- renderTable({values$input})
})
My problem is when I upload a new table (test2.csv), I don't know how to erase the previously stored selections (drop* and select* values) and gives back an error message.
observeEvent(input$new,{
values$upload<-read.csv("test2.csv")
})
}
shinyApp(ui=ui, server = server)
I suppose I should stop somehow the lapply loop and restart it over, so the previously stored values are replaced depending on the new selection, but I am a bit stuck on how I could achieve that.
Just in case you might still be looking for solutions, I wanted to share something that was similar and could potentially be adapted for your needs.
This uses observeEvent for all select inputs. If it detects any changes, it will update all inputs, including the possibilities for select based on drop.
In addition, when a new file is read, the selectInput for drop and select are reset to first value.
Edit: I forgot to keep selected = input[[paste0("drop",i)]] in place for the dropdown (see revised code). It seems to keep the values now when new filters are added - let me know if this is what you had in mind.
library(shiny)
library(shinydashboard)
library(dplyr)
myDataFrame <- read.csv("test.csv")
ui <- shinyUI(
pageWithSidebar(
headerPanel("Testing of dynamic number of selection"),
sidebarPanel(
fileInput("file1", "Choose file to upload", accept = ".csv"),
uiOutput("buttons")
),
mainPanel(
uiOutput("inputs"),
tableOutput("table")
)
)
)
server <- function(input, output, session) {
myInputs <- reactiveValues(rendered = c(1))
myData <- reactive({
inFile <- input$file1
if (is.null(inFile)) {
d <- myDataFrame
} else {
d <- read.csv(inFile$datapath)
}
d
})
observeEvent(lapply(paste0("drop", myInputs$rendered), function(x) input[[x]]), {
for (i in myInputs$rendered) {
updateSelectInput(session,
paste0('select', i),
choices = myData()[input[[paste0('drop', i)]]],
selected = input[[paste0("select",i)]])
}
})
output$buttons <- renderUI({
div(
actionButton(inputId = "add", label = "Add"),
actionButton(inputId = "calc", label = "Calculate")
)
})
observeEvent(input$add, {
myInputs$rendered <- c(myInputs$rendered, max(myInputs$rendered)+1)
})
observeEvent(input$calc, {
showData <- NULL
for (i in 1:length(myInputs$rendered)) {
if(!is.null(input[[paste0("select",i)]])) {
if(is.null(showData)) {
showData <- filter(myData(), myData()[,input[[paste0("drop",i)]]] %in% input[[paste0("select",i)]])
}
else {
showData <- filter(showData, showData[,input[[paste0("drop",i)]]] %in% input[[paste0("select",i)]])
}
}
}
if (is.null(showData)) { showData <- myData() }
output$table <- renderTable({showData})
})
observe({
output$inputs <- renderUI({
rows <- lapply(myInputs$rendered, function(i){
fluidRow(
column(6, selectInput(paste0('drop',i),
label = "",
choices = colnames(myData()),
selected = input[[paste0("drop",i)]])),
column(6, selectInput(paste0('select',i),
label = "",
choices = myData()[1],
multiple = TRUE,
selectize = TRUE))
)
})
do.call(shiny::tagList, rows)
})
})
}
shinyApp(ui, server)