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))
Related
I am not able to delete the generated map after I press the reset button on shiny, could you help me to insert the code to delete the map made after pressing the button? For both selectInput works normally, only the map that is not deleted from the screen.
library(shiny)
library(shinythemes)
library(lubridate)
library(googleway)
set_key("API KEY")
df1<- structure(
list(
Marketname = c("Market1","Market1", "Market2","Market2", "Market3", "Market3", "Market4", "Market4"),
Latitude = c(-22.900200453490385, -22.900200453490385,-22.89279876292728,-22.89279876292728,-22.89107669207457,-22.89107669207457,-22.91668421655409,-22.91668421655409),
Longitude = c(-48.448779371935494,-48.448779371935494, -48.45043377250408,-48.45043377250408,-48.44108027972275,-48.44108027972275,-48.43786997555729,-48.43786997555729)),
row.names = c(NA, 8L), class = "data.frame")
ui <- fluidPage(
shiny::navbarPage(theme = shinytheme("flatly"), collapsible = TRUE,
br(),
tabPanel("Rota",
sidebarLayout(
sidebarPanel(
selectizeInput("market1", label = h5("Choose starting point:"), choices = NULL,
multiple = TRUE,
options = list(maxItems = 1)),
selectizeInput("market2", label = h5("Choose destination point:"), choices = NULL,
multiple = TRUE,
options = list(maxItems = 1)),
actionButton(inputId = "getRoute", label = "Get route"),
actionButton(inputId = "reset", label = "Reset")),
mainPanel(
tabsetPanel(
tabPanel("Route",google_mapOutput(outputId = "mapWarsaw"),
)
))
))))
server <- function(input, output,session) {
observe({
updateSelectizeInput(session, "market1",
choices = unique(df1$Marketname)
)
})
observe({
excludeOption <- NULL
if (!is.null(input$market1)) {
excludeOption <- input$market1
}
updateSelectizeInput(session, "market2",
choices = unique(df1$Marketname[df1$Marketname != excludeOption])
)
})
observeEvent(input$getRoute, {
origin <- df1[df1$Marketname == input$market1, c("Latitude", "Longitude")][1, ]
destination <- df1[df1$Marketname == input$market2, c("Latitude", "Longitude")][1, ]
route <- google_directions(origin = origin,
destination = destination,
mode = "driving")
df_routes <- data.frame(polyline = direction_polyline(route))
df_way <- cbind(
route$routes$legs[[1]]$end_location,
data.frame(address = route$routes$legs[[1]]$end_address)
)
m3<-google_map() %>%
add_polylines(data = df_routes, polyline = "polyline", stroke_weight = 4)
output$mapWarsaw <- renderGoogle_map({
m3
})
})
observeEvent(input$reset, {
updateSelectInput(session, "market1", selected = "")
updateSelectInput(session, "market2", selected = "")
})
}
shinyApp(ui = ui, server = server)
Here I pressed reset, the selectInput was cleared, but the map was not, so I would like to insert some code that cleared the screen. Of course, after inserting the selectInput options again, the map was generated normally on the screen.
I recommend using a reactiveVal to store whether you want to plot or not, then req(.) that value in your plot.
A simple example:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
numericInput("n", "N", value = 2),
actionButton("toggle", "Toggle")
),
mainPanel(
plotOutput("plot")
)
)
)
server <- function(input, output, session) {
doplot <- reactiveVal(TRUE)
observeEvent(input$toggle, {
doplot(!isTRUE(doplot()))
})
output$plot <- renderPlot({
req(doplot())
plot(runif(input$n))
})
}
shinyApp(ui, server)
doplot defaults to TRUE, so it starts plotting immediately (assuming input$n has a value), and every time input$n is changed, a new plot is rendered;
when you click on toggle, the doplot is inverted, and the dependent output$plot will "fail" the req(doplot()) requirement and clear the plot;
this could be improved to give a clearer indication that the empty plot is because the user toggled the button ... in this case, I should likely change the style of the button to clearly indicate the state, but that's aesthetics and may or may not apply to your app
Another similar method would be to store the data in a reactiveVal, and require it, perhaps like this:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
numericInput("n", "N", value = 2),
actionButton("toggle", "Toggle")
),
mainPanel(
plotOutput("plot")
)
)
)
server <- function(input, output, session) {
mydat <- reactiveVal(NULL)
observeEvent(input$toggle, {
req(input$n)
if (is.null(mydat())) {
mydat(runif(input$n))
} else mydat(NULL)
})
output$plot <- renderPlot({
plot(req(mydat()))
})
}
shinyApp(ui, server)
This second method won't work as well if you need to preserve the data.
I have two selectInput boxes in my ShinyApp. Both of them take the same inputs, i.e., the column names of an uploaded table.
I want to make the two input box mutually exclusive, meaning if a column name is selected in one input box, it will become unavailable in the second input box, and vice versa.
Here is my code, and it works.
library(shiny)
ui <- fluidPage(
fileInput(inputId = "rawFile",
label = "Upload Data Table:",
multiple = FALSE,
accept = c(".csv")
),
uiOutput(outputId = "v1",
label = "Select Variable 1"
),
uiOutput(outputId = "v2",
label = "Select Variable 2"
)
)
server <- function(input, output, session){
inputData <- reactive({
inFile <- input$rawFile
if(is.null(inFile)){return(NULL)}
extension <- tools::file_ext(inFile$name)
filepath <- inFile$datapath
df <- read.csv(filepath, header = TRUE)
return(df)
})
output$v1 <- renderUI({
shiny::req(inputData())
selectInput(inputId = "v1",
label = "Select columns to remove",
multiple = TRUE,
choices = names(inputData())
)
})
output$v2 <- renderUI({
shiny::req(inputData())
selectInput(inputId = "v2",
label = "Select columns to remove",
multiple = TRUE,
choices = names(inputData())
)
})
observe({
if(!is.null(input$v2))
updateSelectInput(session, "v1",
choices = names(inputData())[!(names(inputData()) %in% input$v2)],
selected = isolate(input$v1)
)
})
observe({
if(!is.null(input$v1))
updateSelectInput(session, "v2",
choices = names(inputData())[!(names(inputData()) %in% input$v1)],
selected = isolate(input$v2)
)
})
}
shinyApp(ui = ui, server = server)
But when I put this code in a module, it is not working. I don't where the problem is.
library(shiny)
ui_1 <- function(id){
ns <- NS(id)
tagList(
fluidPage(
fileInput(inputId = ns("rawFile"),
label = "Upload Data Table:",
multiple = FALSE,
accept = c(".csv")
),
uiOutput(outputId = ns("v1"),
label = "Select Variable 1"
),
uiOutput(outputId = ns("v2"),
label = "Select Variable 2"
)
)
)
}
server_1 <- function(id){
moduleServer( id, function(input, output, session){
ns <- session$ns
inputData <- reactive({
inFile <- input$rawFile
if(is.null(inFile)){return(NULL)}
extension <- tools::file_ext(inFile$name)
filepath <- inFile$datapath
df <- read.csv(filepath, header = TRUE)
return(df)
})
output$v1 <- renderUI({
shiny::req(inputData())
selectInput(inputId = ns("v1"),
label = "Select columns to remove",
multiple = TRUE,
choices = names(inputData())
)
})
output$v2 <- renderUI({
shiny::req(inputData())
selectInput(inputId = ns("v2"),
label = "Select columns to remove",
multiple = TRUE,
choices = names(inputData())
)
})
observe({
if(!is.null(input$v2))
updateSelectInput(session, ns("v1"),
choices = names(inputData())[!(names(inputData()) %in% input$v2)],
selected = isolate(input$v1)
)
})
observe({
if(!is.null(input$v1))
updateSelectInput(session, ns("v2"),
choices = names(inputData())[!(names(inputData()) %in% input$v1)],
selected = isolate(input$v2)
)
})
}
)
}
The issue is that you wrapped the input id's in ns() inside your updateSelectInputs. You have to do so in renderUI only.
Note: I replaced the code to read a file with mtcars.
library(shiny)
ui_1 <- function(id) {
ns <- NS(id)
tagList(
fluidPage(
fileInput(
inputId = ns("rawFile"),
label = "Upload Data Table:",
multiple = FALSE,
accept = c(".csv")
),
uiOutput(
outputId = ns("v1"),
label = "Select Variable 1"
),
uiOutput(
outputId = ns("v2"),
label = "Select Variable 2"
)
)
)
}
server_1 <- function(id) {
moduleServer(id, function(input, output, session) {
ns <- session$ns
inputData <- reactive({
mtcars
})
output$v1 <- renderUI({
shiny::req(inputData())
selectInput(
inputId = ns("v1"),
label = "Select columns to remove",
multiple = TRUE,
choices = names(inputData())
)
})
output$v2 <- renderUI({
shiny::req(inputData())
selectInput(
inputId = ns("v2"),
label = "Select columns to remove",
multiple = TRUE,
choices = names(inputData())
)
})
observe({
if (!is.null(input$v2)) {
updateSelectInput(session, "v1",
choices = names(inputData())[!(names(inputData()) %in% input$v2)],
selected = isolate(input$v1)
)
}
})
observe({
if (!is.null(input$v1)) {
updateSelectInput(session, "v2",
choices = names(inputData())[!(names(inputData()) %in% input$v1)],
selected = isolate(input$v2)
)
}
})
})
}
ui <- fluidPage(
ui_1("foo")
)
server <- function(input, output, session) {
server_1("foo")
}
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)
Consider the following example application:
library(shiny)
library(shinyWidgets)
module_UI <- function(id){
tagList(
div(
uiOutput(
outputId = NS(id, "selection")
),
shinyWidgets::dropdown(
uiOutput(outputId = NS(id, "new_option")),
style = "unite",
label = "New",
color = "primary",
animate = animateOptions(
enter = animations$fading_entrances$fadeInLeftBig,
exit = animations$fading_exits$fadeOutRightBig
),
up = F,
width = "600px",
inline = T
)
)
)
}
module_server <- function(id){
moduleServer(id, function(input, output, session){
ns <- session$ns
return_values <- reactiveValues(selection=NULL)
output$selection <- renderUI({
selectInput(inputId = ns("selection"), label = "Select:", choices = 1:5)
})
output$new_option <- renderUI({
div(
numericInput(ns("new_option_input"), label = "Add a new option:"),
shinyWidgets::actionBttn(
inputId = ns("submit_new_option"),
label = "Submit",
icon = icon("paper-plane"))
)
})
observeEvent(input$submit_new_option, {
#does not work as intended
updateSelectInput(session = session, inputId = "selection", selected = input$new_option_input)
})
observe({
return_values$selection <- input$selection
})
return(return_values)
})
}
# Define UI for application that draws a histogram
ui <- fluidPage(
title = "Test App",
module_UI("test"),
verbatimTextOutput(outputId = "selection_chosen")
)
# Define server logic required to draw a histogram
server <- function(input, output) {
picker <- module_server("test")
output$selection_chosen <- renderText({
picker$selection
})
}
# Run the application
shinyApp(ui = ui, server = server)
Basically, the module should do two things:
Allow user to select a pre-existing option --> return that value from module
Allow user to create their own, new option --> return that value from module
I have #1 working, but am struggling on #2. Specifically, where I have the "does not work" comment. How can I achieve this functionality? What are/is the best practice(s) for returning server-side created values from a Shiny module? This is an example app; the real one involves reading the selectInput options from a database, as well as saving the newly created options in the database. Appreciate any help on this! A lot of SO answers regarding Shiny modules have the older callModule(...) syntax, which makes researching this topic a bit more confusing.
You just need to provide the default value in numericInput. Perhaps you are looking for this.
library(shiny)
library(shinyWidgets)
module_UI <- function(id){
ns <- NS(id)
tagList(
div(
uiOutput(
outputId = NS(id, "selection")
),
shinyWidgets::dropdown(
uiOutput(outputId = NS(id, "new_option")),
style = "unite",
label = "New",
color = "primary",
animate = animateOptions(
enter = animations$fading_entrances$fadeInLeftBig,
exit = animations$fading_exits$fadeOutRightBig
),
up = F,
width = "600px",
inline = T
),
DTOutput(ns("t1"))
)
)
}
module_server <- function(id){
moduleServer(id, function(input, output, session){
ns <- session$ns
return_values <- reactiveValues(selection=NULL,myiris = iris)
output$selection <- renderUI({
selectInput(inputId = ns("selection"), label = "Select:", choices = 1:5)
})
output$new_option <- renderUI({
tagList(
numericInput(ns("new_option_input"), label = "Add a new option:",10, min = 1, max = 100),
shinyWidgets::actionBttn(
inputId = ns("submit_new_option"),
label = "Submit",
icon = icon("paper-plane"))
)
})
observeEvent(input$submit_new_option, {
return_values$myiris <- iris[1:input$new_option_input,]
#does work as intended
updateSelectInput(session = session, inputId = "selection", choices= c(1:input$new_option_input), selected = input$new_option_input)
})
output$t1 <- renderDT({return_values$myiris})
observe({
return_values$selection <- input$selection
})
return(return_values)
})
}
# Define UI for application that draws a histogram
ui <- fluidPage(
title = "Test App",
module_UI("test"),
verbatimTextOutput(outputId = "selection_chosen"),
DTOutput("t2")
)
# Define server logic required to draw a histogram
server <- function(input, output) {
picker <- module_server("test")
output$selection_chosen <- renderText({
picker$selection
})
output$t2 <- renderDT({picker$myiris[,c(3:5)]})
}
# Run the application
shinyApp(ui = ui, server = server)
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.