I would like to update the options for the select input when someone chooses to filter for the cylinders. However, whenever I update the options in the select input by filtering for cylinders, the reactive fires two times. How can I avoid that?
library(tidyverse)
library(shiny)
library(DT)
data("mtcars")
mtcars <- mtcars %>% tibble::rownames_to_column(var = "cars")
ui <- fluidPage(
shiny::selectInput(
inputId = "cars",
label = "Cars",
choices = mtcars$cars,
selected = mtcars$cars,
multiple = TRUE
),
shiny::checkboxGroupInput(
inputId = "cyl",
label = "Cyl",
choices = unique(mtcars$cyl),
selected = unique(mtcars$cyl)
),
DT::dataTableOutput(outputId = "table")
)
server <- function(session, input, output) {
temp <- shiny::reactive({
temp <- mtcars %>%
dplyr::filter(cars %in% input$cars, cyl %in% input$cyl)
print("Reactive fires twice")
return(temp)
})
shiny::observeEvent(input$cyl, {
shiny::updateSelectInput(
session,
inputId = "cars",
choices = temp()$cars,
selected = temp()$cars
)
})
output$table <- DT::renderDataTable({
temp()
})
}
This solution uses reactive values and I believe avoids the double trigger as it separates trigger events.
library(tidyverse)
library(shiny)
library(DT)
data("mtcars")
mtcars <- mtcars %>% rownames_to_column(var = "cars")
ui <- fluidPage(
selectInput(
inputId = "cars",
label = "Cars",
choices = mtcars$cars,
selected = mtcars$cars,
multiple = TRUE
),
checkboxGroupInput(
inputId = "cyl",
label = "Cyl",
choices = unique(mtcars$cyl),
selected = unique(mtcars$cyl)
),
dataTableOutput(outputId = "table")
)
server <- function(session, input, output) {
r <- reactiveValues(
temp = mtcars
)
observeEvent(input$cyl, ignoreNULL = FALSE, {
r$temp <- mtcars %>%
filter(cyl %in% input$cyl)
updateSelectInput(session,"cars",choices = r$temp$cars, selected = r$temp$cars)
print(input$cyl)
})
observeEvent(input$cars, ignoreNULL = FALSE, {
r$temp <- mtcars %>%
filter(cars %in% input$cars)
})
output$table <- DT::renderDataTable({
r$temp
})
}
shinyApp(ui,server)
Here is a solution using a reactive value instead of a reactive conductor, a priority level for the observers, and freezeReactiveValue:
library(shiny)
library(DT)
data("mtcars")
mtcars <- mtcars %>% tibble::rownames_to_column(var = "cars")
ui <- fluidPage(
selectInput(
inputId = "cars",
label = "Cars",
choices = mtcars[["cars"]],
selected = mtcars[["cars"]],
multiple = TRUE
),
checkboxGroupInput(
inputId = "cyl",
label = "Cyl",
choices = unique(mtcars[["cyl"]]),
selected = unique(mtcars[["cyl"]])
),
DTOutput(outputId = "table")
)
server <- function(session, input, output) {
Temp <- reactiveVal()
observeEvent(list(input[["cars"]], input[["cyl"]]), {
temp <- mtcars %>%
dplyr::filter(cars %in% input[["cars"]], cyl %in% input[["cyl"]])
Temp(temp)
}, priority = 2) # higher priority than the other observer
observeEvent(input[["cyl"]], {
freezeReactiveValue(input, "cars") # prevents the above observer to trigger
updateSelectInput(
session,
inputId = "cars",
choices = mtcars[["cars"]], # don't use Temp() here, otherwise you can't select the removed items
selected = Temp()[["cars"]]
)
}, priority = 1)
output[["table"]] <- renderDT({
Temp()
})
}
shinyApp(ui, server)
Related
I have a code that once I click on the option of my selectInput widget the input value is the names that are showed on the options.
I would like to make the same thing with my actionLink button but the input in this case is the sum of clicks. Is it possible to change the inputs values?
This is my code:
library(shiny)
library(dplyr)
library(purrr)
ui <- fluidPage(
tags$div(
id = "sidebar",
class = "sidebar",
selectInput(
inputId = "custom_select",
label = "Clubs",
choices = names(mtcars),
selectize = F,
size = 5,
width = "300px"
),
div(
names(mtcars) %>% map(~.x %>% actionLink(inputId = .x)))
),
h1(htmlOutput(outputId = 'title')),
h1(htmlOutput(outputId = 'title2')))
server <- function(input, output, session) {
output$title <- renderUI({
input$custom_select
})
output$title2 <- renderUI({
input[[names(mtcars)[1]]]
})
}
shinyApp(ui, server)
As you can see the output is the number of clicks.
For the selectInput widget it works fine.
Any help?
Not sure whether I got you right but using an observeEvent you could do:
library(shiny)
library(dplyr)
library(purrr)
ui <- fluidPage(
tags$div(
id = "sidebar",
class = "sidebar",
selectInput(
inputId = "custom_select",
label = "Clubs",
choices = names(mtcars),
selectize = F,
size = 5,
width = "300px"
),
div(
names(mtcars) %>% map(~ .x %>% actionLink(inputId = .x))
)
),
h1(htmlOutput(outputId = "title")),
h1(htmlOutput(outputId = "title2"))
)
server <- function(input, output, session) {
output$title <- renderUI({
input$custom_select
})
lapply(names(mtcars), function(x) {
observeEvent(input[[x]], {
output$title2 <- renderUI({
paste(x, input[[x]], sep = ": ")
})
})
})
}
shinyApp(ui, server)
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)
I have a min reprex below. I have two tabs and I want the data to only load in the second tab when an user clicks on the second tab. The actual data in the second tab comes from an API so I only want it to load upon clicking (and not everytime the dashboard is loaded).
I want the data to load and give users a choice to add to it, by appending a row to the dataset.
For this reprex I have used iris dataset. I have used reactiveValues, and this seems to work fine except for one problem. It doesn't lazy load, the iris datasets get loaded when the dashboard is loaded (without having to navigate to the second tab).
library(shiny)
library(dplyr)
ui <- fluidPage(
navlistPanel(
tabPanel(
title = "Main Page" # Empty
)
,tabPanel(
title = "Iris"
,fluidRow(
column(
width = 6
,uiOutput(outputId = "choose_species")
)
,column(
width = 6
,uiOutput(outputId = "add_species")
,uiOutput(outputId = "add_measure")
,uiOutput(outputId = "ok")
)
)
,fluidRow(
column(
width = 6
,verbatimTextOutput(outputId = "print_df")
)
)
)
)
)
server <- function(input, output) {
df <- reactiveValues(iris_df = NULL)
observe({
print(is.null(df$iris_df))
})
df$iris_df <- iris %>%
mutate(Species = as.character(Species))
observe({
print(is.null(df$iris_df))
})
output$choose_species <- renderUI({
selectInput(
inputId = "input_choose_species"
,label = "Choose Species"
,choices = df$iris_df %>% distinct(Species)
)
})
output$add_species <- renderUI({
textInput(
inputId = "input_add_species"
,label = "Add Species"
,value = ""
)
})
output$add_measure <- renderUI({
numericInput(
inputId = "input_add_measure"
,label = "Add Measurements"
,value = ""
)
})
output$ok <- renderUI({
actionButton(
inputId = "input_ok"
,label = "Add New Species"
)
})
observeEvent(input$input_ok, {
req(
input$input_add_species
,input$input_add_measure
)
new_row <- c(rep(input$input_add_measure, 4), input$input_add_species)
df$iris_df <- df$iris_df %>% rbind(new_row)
})
output$print_df <- renderPrint({
req(input$input_choose_species)
df$iris_df %>%
filter(Species == input$input_choose_species)
})
}
shinyApp(ui = ui, server = server)
I have tried to solve this issue by using reactive() call instead, but now I am getting this error instead:
server <- function(input, output) {
df <- reactive({
iris %>%
mutate(Species = as.character(Species))
})
output$choose_species <- renderUI({
selectInput(
inputId = "input_choose_species"
,label = "Choose Species"
,choices = df() %>% distinct(Species)
)
})
output$add_species <- renderUI({
textInput(
inputId = "input_add_species"
,label = "Add Species"
,value = ""
)
})
output$add_measure <- renderUI({
numericInput(
inputId = "input_add_measure"
,label = "Add Measurements"
,value = ""
)
})
output$ok <- renderUI({
actionButton(
inputId = "input_ok"
,label = "Add New Species"
)
})
df <- eventReactive(input$input_ok, {
req(
input$input_add_species
,input$input_add_measure
)
new_row <- c(rep(input$input_add_measure, 4), input$input_add_species)
df() %>% rbind(new_row)
})
output$print_df <- renderPrint({
req(input$input_choose_species)
df() %>%
filter(Species == input$input_choose_species)
})
}
shinyApp(ui = ui, server = server)
Warning: Error in : evaluation nested too deeply: infinite recursion / options(expressions=)?
[No stack trace available]
I think I am close and probably missing something really obvious. TIA
I think it should be possible to make it work with reactive(), but it is easy to create an infinite loop, when modifying a reactive expression based on its own value.
An other approach is to use observeEvent() to delay creating a reactiveValue.
library(shiny)
library(dplyr)
ui <- fluidPage(
navlistPanel(id = 'tabs', # set id to allow the server to react to tab change
tabPanel(title = "Main Page" # Empty
)
,tabPanel(title = "Iris" # Title is value if no value is set
,fluidRow(
column(
width = 6
,uiOutput(outputId = "choose_species")
)
,column(
width = 6
,uiOutput(outputId = "add_species")
,uiOutput(outputId = "add_measure")
,uiOutput(outputId = "ok")
)
)
,fluidRow(
column(
width = 6
,verbatimTextOutput(outputId = "print_df")
)
)
)
)
)
server <- function(input, output) {
df = reactiveVal()
observeEvent(input$tabs, {
req(is.null(df()))
if (input$tabs == 'Iris') df(mutate(iris, Species = as.character(Species)))
})
output$choose_species <- renderUI({
req(df())
selectInput(
inputId = "input_choose_species"
,label = "Choose Species"
,choices = df() %>% distinct(Species)
)
})
output$add_species <- renderUI({
textInput(
inputId = "input_add_species"
,label = "Add Species"
,value = ""
)
})
output$add_measure <- renderUI({
numericInput(
inputId = "input_add_measure"
,label = "Add Measurements"
,value = ""
)
})
output$ok <- renderUI({
actionButton(
inputId = "input_ok"
,label = "Add New Species"
)
})
observeEvent(input$input_ok, {
req(
input$input_add_species
,input$input_add_measure
)
new_row <- c(rep(input$input_add_measure, 4), input$input_add_species)
df(df() %>% rbind(new_row))
})
output$print_df <- renderPrint({
req(input$input_choose_species)
df() %>%
filter(Species == input$input_choose_species)
})
}
shinyApp(ui = ui, server = server)
An alternative solution would be to replace your
df$iris_df <- iris %>%
mutate(Species = as.character(Species))
with the below.
observeEvent(input$tabs == "Iris",
{
df$iris_df <- iris %>%
mutate(Species = as.character(Species))
print("Loaded Iris")
},
ignoreInit = TRUE,
once = TRUE
)
As you can see in the console, this causes the dataset to be loaded on tab change, and only once.
I have an shiny app that ask the user to upload a file (a tabulated file with data), then it renders this file into a table and the user can filter some values based on numericInput, selectInput, and textAreaInput. The user has to select the filters and then press a button in order to filter the table.
There is no sequential filtering, i.e, the user can fill all the filters or just one. Every time the user choose a filter the value of the other filters get updated (selectInput inputs) and this is the behaviour I want. However, once the Filter button is pressed, I can't see the previous selection and also I can't reset the filters.
What I would like to achieve is to maintain the actual behaviour when updating the filters, i.e, once I choose a filter and press the filter button the other selectInput choices are automatically updated, BUT I want to keep track of the filters choices, so the user can see the filters he/she has selected. That was what I was expecting but everytime I press the button Filter it seems that the filter tab is rendered again.
Here is my app,
library(shiny)
library(vroom)
library(dplyr)
library(shinycssloaders)
library(shinydashboard)
library(shinydashboardPlus)
library(tidyr)
header <- dashboardHeader()
sidebar <- dashboardSidebar(width = 450,
sidebarMenu(id="tabs",
menuItem("Filtros", tabName="filtros", icon = icon("bar-chart-o")),
uiOutput("filtros")
)
)
body <- dashboardBody(
tabItems(
tabItem(tabName="filtros",
fluidRow(
column(12,dataTableOutput("tabla_julio") %>% withSpinner(color="#0dc5c1"))
)
)
)
)
ui <- dashboardPagePlus(enable_preloader = TRUE, sidebar_fullCollapse = TRUE, header, sidebar, body)
server = function(input, output, session) {
#Create the choices for sample input
vals <- reactiveValues(data=NULL)
vals$data <- iris
output$filtros <- renderUI({
datos <- vals$data
conditionalPanel("input.tabs == 'filtros'",
tagList(
div(style="display: inline-block;vertical-align:top; width: 221px;",numericInput(inputId="Sepal.Length", label="Sepal.Length", value=NA, min = NA, max = NA, step = NA)),
div(
div(style="display: inline-block;vertical-align:top; width: 224px;", selectInput(inputId = "Species", label = "Species", width = "220", choices=unique(datos$Species),
selected = NULL, multiple = TRUE, selectize = TRUE, size = NULL))
)
),
actionButton("filtrar", "Filter")
)
})
# create reactiveValues
vals <- reactiveValues(data=NULL)
vals$data <- iris
# Filter data
observeEvent(input$filtrar, {
tib <- vals$data
if (!is.na(input$Sepal.Length)){
tib <- tib %>% dplyr::filter(!Sepal.Length >= input$Sepal.Length)
print(head(tib))
} else { tib <- tib }
# Filter
if (!is.null(input$Species)){
toMatch <- paste0("\\b", input$Species, "\\b")
matches <- unique(grep(paste(toMatch,collapse="|"), tib$Species, value=TRUE))
tib <- tib %>% dplyr::filter(Species %in% matches)
} else { tib <- tib}
tib -> vals$data
print(head(tib, n=15))
})
# Reactive function creating the DT output object
output$tabla_julio <- DT::renderDataTable({
DT::datatable(vals$data)
})
}
shinyApp(ui, server)
Another Update:
library(shiny)
library(vroom)
library(dplyr)
library(shinycssloaders)
library(shinydashboard)
library(shinydashboardPlus)
library(tidyr)
header <- dashboardHeader()
sidebar <- dashboardSidebar(width = 450,
sidebarMenu(id = "tabs",
menuItem(
"Filtros",
tabName = "filtros",
icon = icon("bar-chart-o")
),
uiOutput("filtros")
))
body <- dashboardBody(tabItems(tabItem(tabName = "filtros",
fluidRow(
column(12,
DT::dataTableOutput("tabla_julio") # %>% withSpinner(color = "#0dc5c1")
)
))))
ui <-
dashboardPagePlus(
enable_preloader = FALSE,
sidebar_fullCollapse = TRUE,
header,
sidebar,
body
)
server = function(input, output, session) {
# Create the choices for sample input
vals <- reactiveValues(data = iris, filtered_data = iris)
output$filtros <- renderUI({
datos <- isolate(vals$data)
conditionalPanel(
"input.tabs == 'filtros'",
tagList(
div(
style = "display: inline-block;vertical-align:top; width: 221px;",
numericInput(
inputId = "SepalLength",
label = "Sepal.Length",
value = NA,
min = NA,
max = NA,
step = NA
)
),
div(
div(
style = "display: inline-block;vertical-align:top; width: 224px;",
selectInput(
inputId = "Species",
label = "Species",
width = "220",
choices = unique(isolate(datos$Species)),
selected = NULL,
multiple = TRUE,
selectize = TRUE,
size = NULL
)
)
)
),
actionButton("filtrar", "Filter", style = "width: 100px;"),
actionButton("reset", "Reset", style = "width: 100px;")
)
})
# Filter data
observeEvent(input$filtrar, {
tib <- vals$data
if (!is.na(input$SepalLength)) {
tib <- tib %>% dplyr::filter(Sepal.Length < input$SepalLength)
print(head(tib))
} else {
tib
}
# Filter
if (!is.null(input$Species)) {
tib <- tib %>% dplyr::filter(Species %in% input$Species)
} else {
tib
}
print(head(tib, n = 15))
vals$filtered_data <- tib
updateSelectInput(session, inputId = "Species", selected = input$Species, choices = unique(vals$filtered_data$Species))
})
observeEvent(input$reset, {
updateNumericInput(session, inputId = "SepalLength", value = NA)
updateSelectInput(session, inputId = "Species", selected = "")
})
# Reactive function creating the DT output object
output$tabla_julio <- DT::renderDataTable({
DT::datatable(vals$filtered_data)
}, server = FALSE)
}
shinyApp(ui, server)
Update: Here is what I think you are after. The most important step is to isolate the inputs in renderUI so they aren't re-rendered on every input change.
library(shiny)
library(vroom)
library(dplyr)
library(shinycssloaders)
library(shinydashboard)
library(shinydashboardPlus)
library(tidyr)
header <- dashboardHeader()
sidebar <- dashboardSidebar(width = 450,
sidebarMenu(id = "tabs",
menuItem(
"Filtros",
tabName = "filtros",
icon = icon("bar-chart-o")
),
uiOutput("filtros")
))
body <- dashboardBody(tabItems(tabItem(tabName = "filtros",
fluidRow(
column(12,
DT::dataTableOutput("tabla_julio") # %>% withSpinner(color = "#0dc5c1")
)
))))
ui <-
dashboardPagePlus(
enable_preloader = FALSE,
sidebar_fullCollapse = TRUE,
header,
sidebar,
body
)
server = function(input, output, session) {
# Create the choices for sample input
vals <- reactiveValues(data = iris, filtered_data = iris)
output$filtros <- renderUI({
datos <- isolate(vals$data)
conditionalPanel(
"input.tabs == 'filtros'",
tagList(
div(
style = "display: inline-block;vertical-align:top; width: 221px;",
numericInput(
inputId = "SepalLength",
label = "Sepal.Length",
value = NA,
min = NA,
max = NA,
step = NA
)
),
div(
div(
style = "display: inline-block;vertical-align:top; width: 224px;",
selectInput(
inputId = "Species",
label = "Species",
width = "220",
choices = unique(isolate(datos$Species)),
selected = NULL,
multiple = TRUE,
selectize = TRUE,
size = NULL
)
)
)
),
actionButton("filtrar", "Filter", style = "width: 100px;"),
actionButton("reset", "Reset", style = "width: 100px;")
)
})
# Filter data
observeEvent(input$filtrar, {
tib <- vals$data
if (!is.na(input$SepalLength)) {
tib <- tib %>% dplyr::filter(Sepal.Length < input$SepalLength)
print(head(tib))
} else {
tib
}
# Filter
if (!is.null(input$Species)) {
tib <- tib %>% dplyr::filter(Species %in% input$Species)
} else {
tib
}
print(head(tib, n = 15))
vals$filtered_data <- tib
})
observeEvent(input$reset, {
updateNumericInput(session, inputId = "SepalLength", value = NA)
updateSelectInput(session, inputId = "Species", selected = "")
})
# Reactive function creating the DT output object
output$tabla_julio <- DT::renderDataTable({
DT::datatable(vals$filtered_data)
}, server = FALSE)
}
shinyApp(ui, server)
Initial answer:
I'd recommend using the selectizeGroup-module from library(shinyWidgets).
It creates a
Group of mutually dependent selectizeInput for filtering
data.frame's columns (like in Excel).
Besides the fact, that it only uses selectizeInput it seems to meet your requirements and saves us from a lot of typing.
Here is an example using the iris dataset:
library(shiny)
library(DT)
library(shinyWidgets)
library(datasets)
DF <- iris
names(DF) <- gsub("\\.", "", names(DF))
ui <- fluidPage(
fluidRow(
column(width = 10, offset = 1, tags$h3("Filter data with selectize group")),
column(width = 3, offset = 1,
selectizeGroupUI(
id = "my-filters",
params = list(
SepalLength = list(inputId = "SepalLength", title = "SepalLength:"),
SepalWidth = list(inputId = "SepalWidth", title = "SepalWidth:"),
PetalLength = list(inputId = "PetalLength", title = "PetalLength:"),
PetalWidth = list(inputId = "PetalWidth", title = "PetalWidth:"),
species = list(inputId = "Species", title = "Species:")
),
inline = FALSE
)),
column(
width = 10, offset = 1,DT::dataTableOutput(outputId = "table")
)
)
)
server <- function(input, output, session) {
filtered_table <- callModule(
module = selectizeGroupServer,
id = "my-filters",
data = DF,
vars = names(DF),
inline = FALSE
)
output$table <- DT::renderDataTable(filtered_table())
}
shinyApp(ui, server)
If i understand your question correctly, you are almost at your goal. In this case, you are overwriting your data at run-time. This causes the filter to be invalid, and the reactive UI seems to check this at every click.
A simple solution is to store the original and filtered datasets separately. An alternativ is to store the filters in a reactive-value and re-render the DataTable at run-time, using the filters on the original table. Here I'll go for the first example.
Below I've changed the following:
Added data_print and filters as reactive values for printing and filters
Changed the filtering method for filtrar, making use of data_print, and added some formatting and changed a few lines of code, as an example of code that might be easier to adapt to a given user-input
removed some unnecesary code (renderDataTable changed input to DT automatically)
server = function(input, output, session) {
#Create the choices for sample input
vals <- reactiveValues(
#raw data
data = iris,
#Exists only in order to print.
data_print = iris,
#for filtering data
filters = list(Species = c(),
Sepal.Length = c()
)
)
#in case of many filters, or filters expanding depending on input data, it might be worth adding this to reactiveValues
## Unchanged
output$filtros <- renderUI({
datos <- vals$data
conditionalPanel("input.tabs == 'filtros'",
tagList(
div(style="display: inline-block;vertical-align:top; width: 221px;",
numericInput(inputId="Sepal.Length", label="Sepal.Length",
value=NA, min = NA, max = NA, step = NA)),
div(
div(style="display: inline-block;vertical-align:top; width: 224px;",
selectInput(inputId = "Species", label = "Species", width = "220",
choices=unique(datos$Species),
selected = NULL, multiple = TRUE, selectize = TRUE, size = NULL))
)
),
actionButton("filtrar", "Filter")
)
})
# Filter data
observeEvent(input$filtrar, {
nm <- names(vals$filters)
for(i in nm){
if(is.na(input[[i]]) || is.null(input[[i]]))
vals$filters[[i]] <- unique(vals$data[[i]]) #If unfiltered use all values
else
vals$filters[[i]] <- input[[i]] #if filtered choose the filtered value
}
#Overwrite data_print instead of data. Creds to https://stackoverflow.com/a/47171513/10782538
vals$data_print <- vals$data %>% dplyr::filter((!!as.symbol(nm[1])) %in% vals$filters[[1]],
(!!as.symbol(nm[2]) %in% vals$filters[[2]]))
})
# Reactive function creating the DT output object
output$tabla_julio <- DT::renderDataTable(
vals$data_print #<====renderDataTable changes to data.
)
}
Unable to make the similar functionality of filters which should be interdependent. So that means if user select a input from one filter, all other filters should get updated.
I have tried multiple ways in shiny but unable to do so however found some code on stackoverflow with similar functionality. The only challenge is that i don't want to show the table as a output and unfortunately the code does not work if we don't pass the output to #tableprint [id of a table].
Any help would be really appreciated.
library(shiny)
library(dplyr)
library(DT)
ui <- fluidPage(
titlePanel("Title"),
sidebarLayout(
sidebarPanel(width=3,
selectInput("filter1", "Filter 1", multiple = TRUE, choices = c("All", LETTERS)),
selectInput("filter2", "Filter 2", multiple = TRUE, choices = c("All", as.character(seq.int(1, length(letters), 1)))),
selectInput("filter3", "Filter 3", multiple = TRUE, choices = c("All", letters)) ),
mainPanel(
DT::dataTableOutput("tableprint")
)
)
)
server <- function(input, output, session) {
output$tableprint <- DT::renderDataTable({
# Data
df <- tibble(LETTERS = rep(LETTERS, 2), Numbers = as.character(1:52),
letters = paste(LETTERS, Numbers, sep = ""))
df1 <- df
if("All" %in% input$filter1){
df1
} else if (length(input$filter1)){
df1 <- df1[which(df1$LETTERS %in% input$filter1),]
}
# Update selectInput choices based on the filtered data. Update 'selected' to reflect the user input.
updateSelectInput(session, "filter1", choices = c("All", df$LETTERS), selected = input$filter1)
updateSelectInput(session, "filter2", choices = c("All", df1$Numbers), selected = input$filter2)
if("All" %in% input$filter2){
df1
} else if (length(input$filter2)){
df1 <- df1[which(df1$Numbers %in% input$filter2),]
}
updateSelectInput(session, "filter3", choices = c("All", df1$letters), selected = input$filter3)
if("All" %in% input$filter3){
df1
} else if (length(input$filter3)){
df1 <- df1[which(df1$letters %in% input$filter3),]
}
datatable(df1)
})
}
# Run the application
shinyApp(ui = ui, server = server)
You can do something like this: its a lot cleaner and easier to read. Note that I added the shinyWidgets package which has the pre-built Select-All Button. You can use the variable called v$df in your other reactives as you said I dont want to show the table as output
library(shiny)
library(dplyr)
library(DT)
library(shinyWidgets)
# Install shinyWidgets
# From CRAN
#install.packages("shinyWidgets")
# From Github
# install.packages("devtools")
#devtools::install_github("dreamRs/shinyWidgets")
df <- tibble(LETTERS = rep(LETTERS, 2), Numbers = as.character(1:52),letters = paste(LETTERS, Numbers, sep = ""))
ui <- fluidPage(
titlePanel("Title"),
sidebarLayout(
sidebarPanel(width=3,
pickerInput("filter1", "Filter 1", choices = LETTERS, options = list(`actions-box` = T), multiple = T),
pickerInput("filter2", "Filter 2", choices = df$Numbers, options = list(`actions-box` = T), multiple = T),
pickerInput("filter3", "Filter 3", choices = letters, options = list(`actions-box` = T), multiple = T)),
mainPanel(
DT::dataTableOutput("tableprint")
)
)
)
server <- function(input, output, session) {
v <- reactiveValues()
observe({
dt <- df$Numbers[df$LETTERS %in% input$filter1]
updatePickerInput(session, "filter2", choices = dt,selected = dt)
})
observe({
dt <- df$letters[df$Numbers %in% input$filter2]
updatePickerInput(session, "filter3", choices = dt,selected = dt)
})
output$tableprint <- DT::renderDataTable({
df <- df[df$LETTERS %in% input$filter1,]
df <- df[df$Numbers %in% input$filter2,]
df <- df[df$letters %in% input$filter3,]
v$df <- df
datatable(df)
})
}
# Run the application
shinyApp(ui = ui, server = server)