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)
Using the diamonds dataset as an example, after a button is pressed, two pickerInput should appear.
In the first one, the user chooses between three columns of the diamonds dataset. Once a value is selected the app should update the choices of the second pickertInput based on the unique values of the selected column.
The app works well without modularizing it. After reading couple of discussions about modules, I still don't clearly understand how to properly declare reactive values for accessing the different input$....
MODULE
module.UI <- function(id){
ns <- NS(id)
actionButton(inputId = ns("add"), label = "Add")
}
module <- function(input, output, session, data, variables){
ns <- session$ns
observeEvent(input$add, {
insertUI(
selector = "#add",
where = "beforeBegin",
ui = fluidRow(
pickerInput(inputId = "picker_variable",
choices = variables,
selected = NULL
),
pickerInput(inputId = "picker_value",
choices = NULL,
selected = NULL
)
)
)
})
observeEvent(input$picker_variable,{
updatePickerInput(session,
inputId = "picker_value",
choices = as.character(unlist(unique(data[, input$picker_variable]))),
selected = NULL
)
})
}
APP
ui <- fluidPage(
mainPanel(
module.UI(id = "myID")
)
)
server <- function(input, output, session) {
callModule(module = module, id = "myID", data = diamonds, variables=c("cut", "color", "clarity"))
}
shinyApp(ui = ui, server = server)
EDIT
User should be able to click the button more than once in order to create several pickerInput pairs.
EDIT #2
Based on #starja code, trying to return the values of the 2 pickers leads to a NULL object.
library(shiny)
library(shinyWidgets)
library(ggplot2)
module.UI <- function(id, variables){
ns <- NS(id)
ui = fluidRow(
pickerInput(inputId = ns("picker_variable"),
choices = variables,
selected = NULL
),
pickerInput(inputId = ns("picker_value"),
choices = NULL,
selected = NULL
)
)
}
module <- function(input, output, session, data, variables){
module_out <- reactiveValues(variable=NULL, values=NULL)
observeEvent(input$picker_variable,{
updatePickerInput(session,
inputId = "picker_value",
choices = as.character(unlist(unique(data[, input$picker_variable]))),
selected = NULL
)
})
observe({
module_out$variable <- input$picker_variable
module_out$values <- input$picker_value
})
return(module_out)
}
ui <- fluidPage(
mainPanel(
actionButton(inputId = "add",
label = "Add"),
tags$div(id = "add_UI_here")
)
)
list_modules <- list()
current_id <- 1
server <- function(input, output, session) {
observeEvent(input$add, {
new_id <- paste0("module_", current_id)
list_modules[[new_id]] <<-
callModule(module = module, id = new_id,
data = diamonds, variables = c("cut", "color", "clarity"))
insertUI(selector = "#add_UI_here",
ui = module.UI(new_id, variables = c("cut", "color", "clarity")))
current_id <<- current_id + 1
})
req(input$list_modules)
print(list_modules)
}
shinyApp(ui = ui, server = server)
EDIT #3
Still having difficulties to return the values of the 2 pickers in a list that would be convenient to access further (example below):
module_out
$module_1
$module_1$variable
[1] "cut"
$module_1$values
[1] "Ideal" "Good"
$module_2
$module_2$variable
[1] "color"
$module_2$values
[1] "E" "J"
Your code has 2 issues:
if you insert UI elements in a module via insertUI, the ids of the UI elements need to have the correct namespace: ns(id)
because the id you use in the selector of insertUI was created in the module, it is also namespaced, so the selector argument also has to be namespaced
library(shiny)
library(shinyWidgets)
library(ggplot2)
module.UI <- function(id){
ns <- NS(id)
actionButton(inputId = ns("add"), label = "Add")
}
module <- function(input, output, session, data, variables){
ns <- session$ns
observeEvent(input$add, {
insertUI(
selector = paste0("#", ns("add")),
where = "beforeBegin",
ui = fluidRow(
pickerInput(inputId = ns("picker_variable"),
choices = variables,
selected = NULL
),
pickerInput(inputId = ns("picker_value"),
choices = NULL,
selected = NULL
)
)
)
})
observeEvent(input$picker_variable,{
updatePickerInput(session,
inputId = "picker_value",
choices = as.character(unlist(unique(data[, input$picker_variable]))),
selected = NULL
)
})
}
ui <- fluidPage(
mainPanel(
module.UI(id = "myID")
)
)
server <- function(input, output, session) {
callModule(module = module, id = "myID", data = diamonds, variables=c("cut", "color", "clarity"))
}
shinyApp(ui = ui, server = server)
BTW: I feel that a more natural way to modularise your code would be that the Add button is in the main app and then dynamically inserts an instance of your module, so that your module only contains the logic/UI for one combination picker_variable/picker_value
Edit
Thanks for your remark. In fact, it doesn't make much sense to create several pickerInput in the module with the same inputId. I've changed my code to reflect the pattern that the actionButton is in the main app and every module only contains one set of inputs:
library(shiny)
library(shinyWidgets)
library(ggplot2)
module.UI <- function(id, variables){
ns <- NS(id)
ui = fluidRow(
pickerInput(inputId = ns("picker_variable"),
choices = variables,
selected = NULL
),
pickerInput(inputId = ns("picker_value"),
choices = NULL,
selected = NULL
)
)
}
module <- function(input, output, session, data, variables){
observeEvent(input$picker_variable,{
updatePickerInput(session,
inputId = "picker_value",
choices = as.character(unlist(unique(data[, input$picker_variable]))),
selected = NULL
)
})
}
ui <- fluidPage(
mainPanel(
actionButton(inputId = "add",
label = "Add"),
tags$div(id = "add_UI_here")
)
)
list_modules <- list()
current_id <- 1
server <- function(input, output, session) {
observeEvent(input$add, {
new_id <- paste0("module_", current_id)
list_modules[[new_id]] <<-
callModule(module = module, id = new_id,
data = diamonds, variables = c("cut", "color", "clarity"))
insertUI(selector = "#add_UI_here",
ui = module.UI(new_id, variables = c("cut", "color", "clarity")))
current_id <<- current_id + 1
})
}
shinyApp(ui = ui, server = server)
Edit 2
You can directly return the input from the module and use this in a reactive context in the main app:
library(shiny)
library(shinyWidgets)
library(ggplot2)
module.UI <- function(id, variables){
ns <- NS(id)
ui = fluidRow(
pickerInput(inputId = ns("picker_variable"),
choices = variables,
selected = NULL
),
pickerInput(inputId = ns("picker_value"),
choices = NULL,
selected = NULL
)
)
}
module <- function(input, output, session, data, variables){
observeEvent(input$picker_variable,{
updatePickerInput(session,
inputId = "picker_value",
choices = as.character(unlist(unique(data[, input$picker_variable]))),
selected = NULL
)
})
return(input)
}
ui <- fluidPage(
mainPanel(
actionButton(inputId = "print", label = "print inputs"),
actionButton(inputId = "add",
label = "Add"),
tags$div(id = "add_UI_here")
)
)
list_modules <- list()
current_id <- 1
server <- function(input, output, session) {
observeEvent(input$add, {
new_id <- paste0("module_", current_id)
list_modules[[new_id]] <<-
callModule(module = module, id = new_id,
data = diamonds, variables = c("cut", "color", "clarity"))
insertUI(selector = "#add_UI_here",
ui = module.UI(new_id, variables = c("cut", "color", "clarity")))
current_id <<- current_id + 1
})
observeEvent(input$print, {
lapply(seq_len(length(list_modules)), function(i) {
print(names(list_modules)[i])
print(list_modules[[i]]$picker_variable)
print(list_modules[[i]]$picker_value)
})
})
}
shinyApp(ui = ui, server = server)
The code worked only when I added variables and removed for the first time. After I removed that variable, it went back to the select "Add to the plot", then I couldn't add it back, I think when I updated updateSelectInput, there is something wrong. Plus selectRemove needed to remove when it NULL. How I can update by both ways?
library(shiny)
mtcars_1 <- mtcars[,c("mpg", "disp", "hp", "drat", "wt", "qsec")]
runApp(list(
ui=pageWithSidebar(headerPanel("Adding and Removing Variables"),
sidebarPanel(
selectInput(inputId = "selectAdd", label = "Add to the plot",
choices = c(names(mtcars_1)),
selected = names(mtcars_1)[1]),
actionButton(inputId = "add", label = "Add to the plot")
),
mainPanel(
textOutput("text"),hr(),
uiOutput("remove_list")
)
),
server=function(input, output, session) {
rv <- reactiveValues(add_v = c())
observeEvent(input$add,{
rv$add_v <- rbind(rv$add_v,input$selectAdd)
})
rv <- reactiveValues(rem_v = c())
observeEvent(input$remove,{
rv$rem_v <- rbind(rv$rem_v,input$selectRemove)
})
observe({
value_add <- c(names(mtcars_1)[!names(mtcars_1) %in% rv$add_v ],rv$rem_v)
value_rem <-c(rv$add_v[! rv$add_v %in% rv$rem_v])
updateSelectInput(session,"selectAdd",choices = value_add)
updateSelectInput(session,"selectRemove",choices = value_rem)
})
output$remove_list <- renderUI({
if(length(rv$add_v) > 0){
tagList(
selectInput(inputId = "selectRemove", label = "Remove to the plot",
choices = c(rv$add_v),
selected = rv$add_v[1]),
actionButton(inputId = "remove", label = "Remove to the plot")
)
}
})
output$text <- renderText({
c(rv$add_v[! rv$add_v %in% rv$rem_v])
})
}))
Welcome to stackoverflow!
You were almost there - however, your updating logic for the reactiveValues wasn't complete. For each button click you'll have to add an object to one value and remove it from the other. Please check the following:
library(shiny)
mtcars_1 <- mtcars[, c("mpg", "disp", "hp", "drat", "wt", "qsec")]
runApp(list(
ui = pageWithSidebar(
headerPanel("Adding and Removing Variables"),
sidebarPanel(
selectInput(
inputId = "selectAdd",
label = "Add to the plot",
choices = names(mtcars_1),
selected = names(mtcars_1)[1]
),
actionButton(inputId = "add", label = "Add to the plot")
),
mainPanel(textOutput("text"), hr(),
uiOutput("remove_list"))
),
server = function(input, output, session) {
rv <- reactiveValues(add_v = NULL, rem_v = names(mtcars_1))
observeEvent(input$add, {
rv$rem_v <- setdiff(rv$rem_v, input$selectAdd)
rv$add_v <- union(rv$add_v, input$selectAdd)
})
observeEvent(input$remove, {
rv$add_v <- setdiff(rv$add_v, input$selectRemove)
rv$rem_v <- union(rv$rem_v, input$selectRemove)
})
observe({
updateSelectInput(session, "selectAdd", choices = rv$rem_v)
updateSelectInput(session, "selectRemove", choices = rv$add_v)
})
output$remove_list <- renderUI({
if (length(rv$add_v) > 0) {
tagList(
selectInput(
inputId = "selectRemove",
label = "Remove to the plot",
choices = c(rv$add_v),
selected = rv$add_v[1]
),
actionButton(inputId = "remove", label = "Remove to the plot")
)
}
})
output$text <- renderText({
c(rv$add_v[!rv$add_v %in% rv$rem_v])
})
}
))
I'm building a shinyApp on mtcars data. I got 2 actionButtons (Go & Clear).
The Go button is for displaying the output on mainPanel whereas the Clear button is for clearing that output.
My Clear button isn't working due to some unforeseen reason. Can somebody please have a look at my codes. I shall be extremely grateful.
library(shiny)
library(DT)
library(dplyr)
library(shinythemes)
library(htmlwidgets)
library(shinyWidgets)
library(shinydashboard)
data_table<-mtcars
#ui
ui = fluidPage(
sidebarLayout(
sidebarPanel (
uiOutput("cyl_selector"),
uiOutput("disp_selector"),
actionButton(inputId = "go", label = "Go"),
actionButton(inputId = "reset", label = "Clear")),
mainPanel(
DT::dataTableOutput('mytable') )))
#server
server = function(input, output, session) {
output$cyl_selector <- renderUI({
selectInput(inputId = "cyl",
label = "cyl:", multiple = TRUE,
choices = c( unique(as.character(data_table$cyl))),
selected = c('4')) })
output$disp_selector <- renderUI({
available <- data_table[c(data_table$cyl %in% input$cyl ), "disp"]
selectInput(
inputId = "disp",
label = "disp:",
multiple = TRUE,
choices = c('All',as.character(unique(available))),
selected = 'All') })
thedata <- eventReactive(input$go,{
data_table<-data_table[data_table$cyl %in% input$cyl,]
if(input$disp != 'All'){
data_table<-data_table[data_table$disp %in% input$disp,]
}
data_table
})
# thedata <- eventReactive(input$reset,{
# data_table<-NULL
# })
output$mytable = DT::renderDataTable({
DT::datatable( filter = "top", rownames = FALSE, escape = FALSE,
options = list(pageLength = 50, autowidth=FALSE,
dom = 'Brtip' ),
{
thedata() # Call reactive thedata()
})
})}
shinyApp(ui = ui, server = server)
insertUI() and removeUI() is what you might be looking for.
Removing the element is easier with removeUI():
observeEvent(input$reset, {
removeUI("#mytable")
})
To avoid that you dont delete it permanently you could use insertUI():
observeEvent(input$go, {
insertUI("#placeholder", "afterEnd", ui = DT::dataTableOutput('mytable'))
})
In order to place the element correctly you can use a placeholder in the mainPanel():
mainPanel(
tags$div(id = "placeholder")
)
Then you could remove the dependency of thedata() from the input button, since you use the insertUI() now. (You should swith to insertUI(), because otherwise you cant re-insert the table once its deleted without it,...)
thedata <- reactive({
...
})
Full example would read:
library(shiny)
library(DT)
library(dplyr)
library(shinythemes)
library(htmlwidgets)
library(shinyWidgets)
library(shinydashboard)
data_table<-mtcars
#ui
ui = fluidPage(
sidebarLayout(
sidebarPanel (
uiOutput("cyl_selector"),
uiOutput("disp_selector"),
actionButton(inputId = "go", label = "Go"),
actionButton(inputId = "reset", label = "Clear")),
mainPanel(
tags$div(id = "placeholder")
)
)
)
#server
server = function(input, output, session) {
output$cyl_selector <- renderUI({
selectInput(inputId = "cyl",
label = "cyl:", multiple = TRUE,
choices = c( unique(as.character(data_table$cyl))),
selected = c('4')) })
output$disp_selector <- renderUI({
available <- data_table[c(data_table$cyl %in% input$cyl ), "disp"]
selectInput(
inputId = "disp",
label = "disp:",
multiple = TRUE,
choices = c('All',as.character(unique(available))),
selected = 'All') })
thedata <- reactive({
input$go
isolate({
data_table<-data_table[data_table$cyl %in% input$cyl,]
if(input$disp != 'All'){
data_table<-data_table[data_table$disp %in% input$disp,]
}
return(data_table)
})
})
observeEvent(input$reset, {
removeUI("#mytable")
})
observeEvent(input$go, {
insertUI("#placeholder", "afterEnd", ui = DT::dataTableOutput('mytable'))
})
output$mytable = DT::renderDataTable({
DT::datatable( filter = "top", rownames = FALSE, escape = FALSE,
options = list(pageLength = 50, autowidth=FALSE,
dom = 'Brtip' ),
{
thedata() # Call reactive thedata()
})
})}
shinyApp(ui = ui, server = server)
)
Why not inject some javascript? This way, your code is kept virtually unchanged.
Create a js file in your shiny folder with the following code (rmDt.js in this example):
$("#reset").click(function() {
$(".display.dataTable.no-footer").DataTable().destroy();
$(".display.dataTable.no-footer").DataTable().clear().draw();
$(".display.no-footer").DataTable().destroy();
$(".display.no-footer").DataTable().clear().draw();
});
Save this file and then inject it in your shiny R script:
library(shiny)
library(DT)
library(dplyr)
library(htmlwidgets)
library(shinyWidgets)
library(shinydashboard)
data_table<-mtcars
#ui
ui = fluidPage(
sidebarLayout(
sidebarPanel (
uiOutput("cyl_selector"),
uiOutput("disp_selector"),
actionButton(inputId = "go", label = "Go"),
actionButton(inputId = "reset", label = "Clear"),
includeScript(path ="rmDt.js") # inject javascript
),
mainPanel(
DT::dataTableOutput('mytable') ))
)
#server
server = function(input, output, session) {
output$cyl_selector <- renderUI({
selectInput(inputId = "cyl",
label = "cyl:", multiple = TRUE,
choices = c( unique(as.character(data_table$cyl))),
selected = c('4')) })
output$disp_selector <- renderUI({
available <- data_table[c(data_table$cyl %in% input$cyl ), "disp"]
selectInput(
inputId = "disp",
label = "disp:",
multiple = TRUE,
choices = c('All',as.character(unique(available))),
selected = 'All') })
thedata <- eventReactive(input$go,{
data_table<-data_table[data_table$cyl %in% input$cyl,]
if(input$disp != 'All'){
data_table<-data_table[data_table$disp %in% input$disp,]
}
data_table
})
output$mytable = DT::renderDataTable({
DT::datatable( filter = "top", rownames = FALSE, escape = FALSE,
options = list(pageLength = 50, autowidth=FALSE,
dom = 'Brtip' ),
{
thedata() # Call reactive thedata()
})
})}
shinyApp(ui = ui, server = server, options = list(launch.browser = T))
I'm building a shinyApp on mtcars data. I got 2 actionButtons (Go & Clear).
The Go button is for displaying the output on mainPanel whereas the Clear button is for clearing that output.
My Clear button isn't working due to some unforeseen reason. Can somebody please have a look at my codes. I shall be extremely grateful.
library(shiny)
library(DT)
library(dplyr)
library(shinythemes)
library(htmlwidgets)
library(shinyWidgets)
library(shinydashboard)
data_table<-mtcars
#ui
ui = fluidPage(
sidebarLayout(
sidebarPanel (
uiOutput("cyl_selector"),
uiOutput("disp_selector"),
actionButton(inputId = "go", label = "Go"),
actionButton(inputId = "reset", label = "Clear")),
mainPanel(
DT::dataTableOutput('mytable') )))
#server
server = function(input, output, session) {
output$cyl_selector <- renderUI({
selectInput(inputId = "cyl",
label = "cyl:", multiple = TRUE,
choices = c( unique(as.character(data_table$cyl))),
selected = c('4')) })
output$disp_selector <- renderUI({
available <- data_table[c(data_table$cyl %in% input$cyl ), "disp"]
selectInput(
inputId = "disp",
label = "disp:",
multiple = TRUE,
choices = c('All',as.character(unique(available))),
selected = 'All') })
thedata <- eventReactive(input$go,{
data_table<-data_table[data_table$cyl %in% input$cyl,]
if(input$disp != 'All'){
data_table<-data_table[data_table$disp %in% input$disp,]
}
data_table
})
# thedata <- eventReactive(input$reset,{
# data_table<-NULL
# })
output$mytable = DT::renderDataTable({
DT::datatable( filter = "top", rownames = FALSE, escape = FALSE,
options = list(pageLength = 50, autowidth=FALSE,
dom = 'Brtip' ),
{
thedata() # Call reactive thedata()
})
})}
shinyApp(ui = ui, server = server)
I didn't analyze your script completly, but i can see that it doesn't call the second button at all (Clear). You made an eventReactive() using input$go for the first button to make the plot, but you need to call input$reset too if you want to make it work.