Observe multiple shiny inputs in a div with an id - r

Problem Statement:
Say I have multiple inputs in a wellPanel which has id = "my_inputs". Below the wellPanel is a button labelled "Calculate".
The button is only enabled if all user inputs are valid ie. Not blanks and Not NULL.
To do that I've had to check the validity of each of them, one by one using:
valid_inputs <- reactive({
inputs <- c(input$input1, input$input2, input$input3)
purrr::map_lgl(.x = inputs, .f = is_valid) |> all()
})
That works fine. The problem is that I have 3 tabs each with 10 inputs and typing out c(input$input1, input$input2, input$input3, ..., input$input10) is kind of tiring considering the inputId's are different and NOT sequential. Also, each tab has its own different inputs so they are not similar.
Question:
Is there a way to check for the inputs' validity using the id of the wellPanel?
Something along the lines of:
is_valid(id = "my_inputs")
Reprex:
library(shiny)
library(shinyjs)
ui <- fluidPage(
shinyjs::useShinyjs(),
wellPanel(
id = "my_inputs",
fluidRow(
column(
width = 4,
selectInput(
inputId = "input1",
label = "Input 1",
choices = letters[1:3]
)
),
column(
width = 4,
textInput(
inputId = "input2",
label = "Input 2",
placeholder = "Enter name"
)
),
column(
width = 4,
selectInput(
inputId = "input3",
label = "Input 3",
choices = c("", letters[4:6])
)
)
)
),
fluidRow(
column(
width = 12,
align = "center",
actionButton(
inputId = "actionbtn",
label = "Calculate",
class = "btn-success"
)
)
)
)
server <- function(input, output, session) {
# fn to check if an input is valid:
is_valid <- function(x) {
x != "" && !is.null(x) # input should NOT be blank or NULL
}
# Map that function over all inputs:
valid_inputs <- reactive({
inputs <- c(input$input1, input$input2, input$input3)
purrr::map_lgl(.x = inputs, .f = is_valid) |> all()
})
# if all inputs are valid, enable calculate btn, else disable:
observe(
shinyjs::toggleState(
id = "actionbtn",
condition = valid_inputs()
)
)
}
shinyApp(ui, server)

In that case you can grab the input ids with reactiveValuesToList, and apply your validation function to each element.
server <- function(input, output, session) {
# Capture all input ids
ui_inputs <- reactive({
x <- names(reactiveValuesToList(input))
x[startsWith(x, "input")] # ignore actionbtn
})
# fn to check if an input is valid:
is_valid <- function(x) {
x != "" && !is.null(x) # input should NOT be blank or NULL
}
# Check if all input ids are valid using your function
valid_inputs <- reactive({
x <- lapply(ui_inputs(), function(x) is_valid(input[[x]]) )
all(x)
})
# if all inputs are valid, enable calculate btn, else disable:
observe(
shinyjs::toggleState(
id = "actionbtn",
condition = valid_inputs()
)
)
}
shinyApp(ui, server)

Here is an alternative approach using tagQuery from library(htmltools) to extract the id's of wellPanel's children.
To test the inputs you can simply use shiny::isTruthy:
library(shiny)
library(shinyjs)
library(htmltools)
ui <- fluidPage(
shinyjs::useShinyjs(),
wellPanel(
id = "wP1",
fluidRow(
column(
width = 4,
selectInput(
inputId = "input1",
label = "Input 1",
choices = letters[1:3]
)
),
column(
width = 4,
textInput(
inputId = "input2",
label = "Input 2",
placeholder = "Enter name"
)
),
column(
width = 4,
selectInput(
inputId = "input3",
label = "Input 3",
choices = c("", letters[4:6])
)
)
)
),
fluidRow(
column(
width = 12,
align = "center",
actionButton(
inputId = "actionbtn",
label = "Calculate",
class = "btn-success"
)
)
)
)
wP1Containers <- tagQuery(ui)$find("#wP1")$find("div.shiny-input-container")
wP1InputTags <- c(wP1Containers$find("input")$selectedTags(), wP1Containers$find("select")$selectedTags())
wP1InputIDs <- sapply(wP1InputTags, tagGetAttribute, attr = "id")
server <- function(input, output, session) {
observe({
valid_inputs <- all(sapply(wP1InputIDs, function(x){isTruthy(input[[x]])}))
shinyjs::toggleState(
id = "actionbtn",
condition = valid_inputs
)
})
}
shinyApp(ui, server)
Here is a related issue.
Another approach would be to use a prefix or suffix to identify those input ID's in session$input via grep and check if isTruthy via lapply.

Related

Dynamically update tag value when dateinput changes

Is it possible to update a tag value when dateInput is changed by a user?
Maybe something like force, eventReactive, reactivevalue or stop lazy evaluation?
library(shiny)
library(lubridate)
library(tidyverse)
library(DT)
data <- data.frame(
date=seq.Date(from=dmy("1/1/2022"),to=dmy("31/1/2022"),by="day"),
y=round(rnorm(n=31,mean=0,sd=2),2)
)
date_selected <- ""
# fn_lookup(date_in="")
fn_lookup <- function(date_in){
if(length(date_in)>0){
y=(data %>% filter(date==dmy(date_in)))$y
}else{
y=""
}
return(y)
}
ui <- fluidPage(
fluidRow(
column(12,actionButton(inputId="edit_table",label="edit",icon=icon("edit")))
),
fluidRow(
br(),
br(),
column(12,DT::dataTableOutput('table')))
)
server <- function(input, output) {
output$table <- DT::renderDataTable(data)
observeEvent(input$edit_table,{
date_selected <- eventReactive(input$ad1, { input$ad1 })
showModal(modalDialog(
tagList(
div(style="display:inline-block;",
dateInput(inputId="ad1",label="Date",value="2022-1-15",format="dd/mm/yyyy")
),
div(style="display:inline-block;",
tags$label("value static"),
tags$input(id = "ad2", type = "text", class="form-control",value = fn_lookup("15/1/2022"))
),
div(style="display:inline-block;",
tags$label("value dynamic"),
#dynamically update this tag value when the date input changes
tags$input(id = "ad3", type = "text", class="form-control",value = fn_lookup(input$ad1))
),
div(style="display:inline-block;",
tags$label("value dynamic 2"),
#dynamically update this tag value when the date input changes
# tags$input(id = "ad3", type = "text", class="form-control",value = fn_lookup(date_selected))
)
),
footer = tagList(
modalButton("Cancel"),
actionButton(inputId="check", "Check",icon=icon("check"))
),
easyClose = TRUE
))
})
}
shinyApp(ui, server)

Error in make.unique: 'names' must be a character vector when switching tabsetPanel in shiny

In the example below I get following error many times printed when switching back and forth between radio buttons iris and About-
Warning: Error in make.unique: 'names' must be a character vector
[No stack trace available]
I have looked for the error, but not much of help is out there besides these (however they're a bit irrelevant):
https://github.com/petzi53/bib2academic/issues/1
https://github.com/satijalab/seurat/issues/1710
how to solve "ERROR: Names must be unique." in r-package ggstatsplot?
Why does it print that I have selected two inputs, even though I have selected only one?
[1] "You have chosen: 1"
[1] "You have chosen: 3" #this should have been NULL??!
Also why is the mainPanel not updating properly when I switch the nav menus?
options(scipen = 99999, stringsAsFactors = FALSE)
library(shiny)
library(shinyjs)
library(shinyWidgets)
library(DT)
library(dplyr)
gen_rep_def <- data.frame(Report = c("iris",
"etc"),
Purpose=c("abc",
"xyz"))
mon_rep_def <- data.frame(Report = c("mtcars",
"etc"),
Purpose= c("abc",
"xyz"))
ui <- fluidPage(
shinyjs::useShinyjs(),
navbarPage(
verbatimTextOutput("value"),
tabPanel("General Reports",
sidebarLayout(
sidebarPanel(
id = "Sidebar",
shinyWidgets::prettyRadioButtons(
inputId = "controller",
label = "Choose:",
choices = c("About"= 1,
"iris"= 2),
icon= icon("check"),
selected = 1,
status = "success",
animation="smooth"
)
),
mainPanel(
id = "main_panel",
tabsetPanel(
id = "hidden_tabs",
type = "hidden",
tabPanelBody(
"panel1", DT::DTOutput('panel1_data')
),
tabPanelBody(
"panel2",
tabsetPanel(
tabPanel("Data", DT::DTOutput('panel2_data'))
)
)
)
)
)
),
# monthly reports
tabPanel("Extra General Reports",
sidebarLayout(
sidebarPanel(
id = "Sidebar_2",
shinyWidgets::prettyRadioButtons(
inputId = "controller_2",
label = "Choose:",
choices = c("About"= 3,
"mtcars"= 4),
icon= icon("check"),
#selected = 3,
status = "success",
animation="smooth"
)
),
mainPanel(
id = "main_panel_2",
tabsetPanel(
id = "hidden_tabs_2",
type = "hidden",
tabPanelBody(
"panel3", DT::DTOutput('panel3_data')
),
tabPanelBody(
"panel4",
tabsetPanel(
tabPanel("Data", DT::DTOutput('panel4_data'))
)
)
)
)
)
)
),
tags$head(tags$style(HTML('.navbar-brand {width: 270px; font-size:35px; text-align:left;
font-family: "serif";')))
)
server <- function(input, output, session) {
observeEvent(input$controller, {
print(paste0("You have chosen: ", input$controller))
})
observeEvent(input$controller_2, {
print(paste0("You have chosen: ", input$controller_2))
})
data_sets <- list(df1 = gen_rep_def,
df2 = iris,
df3 = mon_rep_def,
df4 = mtcars)
data_to_use <- reactiveValues(name = "df", data = data.frame())
observeEvent(input$controller, {
updateTabsetPanel(session, inputId= "hidden_tabs", selected = paste0("panel", input$controller))
req(input$controller)
data_to_use$data <- data_sets[[as.numeric(input$controller)]]
data_to_use$name <- names(data_sets[as.numeric(input$controller)])
output[[paste0('panel', input$controller, '_data')]] <- DT::renderDT(server = FALSE, {
DT::datatable(data_to_use$data,
filter = 'top',
extensions = 'Buttons')})
})
observeEvent(input$controller_2, {
updateTabsetPanel(session, inputId= "hidden_tabs_2", selected = paste0("panel", input$controller_2))
req(input$controller_2)
data_to_use$data <- data_sets[[as.numeric(input$controller_2)]]
data_to_use$name <- names(data_sets[as.numeric(input$controller_2)])
output[[paste0('panel', input$controller_2, '_data')]] <- DT::renderDT(server = FALSE, {
DT::datatable(data_to_use$data,
filter = 'top',
extensions = 'Buttons')})
})
}
shinyApp(ui= ui, server= server)
The error was coming from the esquisse package unfortunately (https://github.com/dreamRs/esquisse/issues/164). It has been resolved now by the developer.
And second part of my question was answered by #bretauv. Thank you again!

R Shiny: Print input from a selectInput function created dynamically from prior inputs

I am struggling to print the output of various selectInput options on my 'Example_2' tab. These fields themselves have been created within the server based on prior inputs from 'Example_1' tab.
Please see below:
library(shinythemes)
library(shiny)
rm(list = ls())
ui <- navbarPage('Example',id = "inTabset",
tabPanel(title = "Example_1", value = "Example_1",
fluidPage(
tags$b( h4("Example_1", align = "left")),
theme = shinytheme("paper"),
fluidRow(
column(6,checkboxGroupInput("checkGroup", label ="",
choices = c(1,2,3,4,5,6,7,8),
selected = c(1,4,7)) )
),
br()
),
hr(),
verbatimTextOutput("example1")
),
tabPanel(title = "Example_2", value = "Example_2",
fluidPage(
tags$b( h4("Example_2", align = "left")),
br(),
fluidRow(
column(4, uiOutput("VarsInput")),
fluidRow(verbatimTextOutput("dataInfo")),
br(),
hr())
)
))
server <- function(input, output, session) {
output$example1 = renderPrint(input$checkGroup)
### output$example2 = ????
### i.e what data (a,b,c,d,e or f) has been chosen from the selectInput below?
K <- reactive({
length(input$checkGroup)
})
output$VarsInput <- renderUI({
NoV = K()
C = sapply(1:(ceiling(NoV)), function(i){paste0(input$checkGroup[i])})
output = tagList()
for(i in seq_along(1:ceiling(NoV))){
output[[i]] = tagList()
output[[i]][[1]] = selectInput(C[i], C[i], c("",c("a","b","c","d","e","f")))
}
output
})
}
shinyApp(ui, server)
In ui I added verbatimTextOutput for your example2.
When dynamically creating outputs, I believe you just need output[[i]] in your for loop.
For name of these selectInput widgets, added "item" instead of just having the id be a number.
Then, you can access the selected values for these inputs through input[[paste0("item", i)]] where i is matched to your checkboxes.
Edit (12/27/20) Based on comment, with varying checkboxes and inputs, you will want to store both the input name (or index) and choice. So, you could make a reactive data frame to store these, instead of just storing the value selected. Also, you need to check if the dynamically created input exists (or is.null) before storing the value. Additionally, when you create your new dynamic inputs, you can check with the index to provide an accurate default/selected value. See if this works for you.
library(shinythemes)
library(shiny)
ui <- navbarPage('Example',id = "inTabset",
tabPanel(title = "Example_1", value = "Example_1",
fluidPage(
tags$b( h4("Example_1", align = "left")),
theme = shinytheme("paper"),
fluidRow(
column(6,checkboxGroupInput("checkGroup", label ="",
choices = c(1,2,3,4,5,6,7,8),
selected = c(1,4,7)) )
),
br()
),
hr(),
verbatimTextOutput("example1")
),
tabPanel(title = "Example_2", value = "Example_2",
fluidPage(
tags$b( h4("Example_2", align = "left")),
br(),
fluidRow(
column(4, uiOutput("VarsInput")),
fluidRow(verbatimTextOutput("dataInfo")),
br(),
hr(),
verbatimTextOutput("example2"))
)
))
server <- function(input, output, session) {
rv <- reactiveValues(df = NULL)
observe({
rv$df <- data.frame(
index = as.numeric(),
choice = as.character()
)
for (i in input$checkGroup) {
the_item <- input[[paste0("item", i)]]
rv$df <- isolate(rbind(rv$df, data.frame(index = i, choice = ifelse(is.null(the_item), "", the_item))))
}
})
output$example1 = renderPrint(input$checkGroup)
output$example2 <- renderPrint(
for (i in input$checkGroup) {
print(input[[paste0("item", i)]])
}
)
K <- reactive({
length(input$checkGroup)
})
output$VarsInput <- renderUI({
NoV = K()
C = sapply(1:(ceiling(NoV)), function(i){paste0(input$checkGroup[i])})
output = tagList()
for(i in seq_along(1:ceiling(NoV))){
output[[i]] = tagList()
output[[i]] = selectInput(paste0("item", C[i]), C[i], c("",c("a","b","c","d","e","f")),
selected = isolate(rv$df$choice[rv$df$index == C[i]]))
}
output
})
}
shinyApp(ui, server)

filter data in shiny app but keeping values in selectInput when updating table

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.
)
}

R Shiny module: input not updated with uiOutput / renderUI inside callModule

I've been searching around and cannot find an answer to my question. I've constructed a simple app to demonstrate my problem. Basically, the problem is that I am trying to use a renderUI inside my module server to conditionally create a uiOutput in the module UI. I've included a few print statements that lead me to believe that the renderUI is evaluated without input being updated. It is killing me that I can't figure this out, and I'd appreciate any help possible!
Example code:
library(shiny) # shiny_1.0.0
library(DT) # DT_0.2
testModuleUI <- function(id) {
ns = NS(id)
tagList(
br(),
sidebarPanel(width = 12, id = "inputBar",
fluidRow(
column(width = 2, checkboxInput(ns("buttonA"), label = "Button A", value = F)),
column(width = 2, uiOutput(ns("getButtonB")))
),
dataTableOutput(outputId = ns("tableOutput"))
)
)
}
testModule <- function(input, output, session, showB = F ){
ns = session$ns
output$getButtonB <- renderUI({
if( showB ){
print("call checkboxInput")
checkboxInput(ns("buttonB"), label = "Button B", value = F)
}else{
NULL
}
})
getTable <- reactive({
print("inside getTable")
out = c()
if( input$buttonA ) {
out = paste0(out, "A")
}
if( input$buttonB ){
out = paste0(out, "B")
}
data.frame(var = out)
})
output$tableOutput <- renderDataTable({
print("call getTable")
datatable( getTable() )
})
}
server <- function(input, output, session) {
callModule( module = testModule, id = "test1", showB = T )
session$onSessionEnded( stopApp )
}
ui <- pageWithSidebar(
headerPanel( title = "Test app" ),
sidebarPanel(
width = 3,
selectInput(inputId = "whatever", label = "This button doesn't matter", choices = c("A", "B"))
),
mainPanel(
tabsetPanel(
tabPanel("Tab 1", testModuleUI("test1"))
)
)
)
shinyApp( ui = ui, server = server, options = list(launch.browser = T)
)
Thank you!!

Resources