Shiny checkbox input tracking - r

I am currently working on an application form using Shiny. My main page will consist of a table that will have a click button which will open a new modal window that will display the application form details. Once the save button is clicked the data will get stored in my DB in the backend. I have given a sample example of my app using mtcars.
My question is a two parter.
I have added checkbox to the main page rows and wish to track the click of these check boxes so that I can check the required ones and using their click event save some other value in the DB. How can I track this checkbox click event?
I need a single checkbox click to select about 150 or more check boxes simultaneously. Is there a way to capture all these click events together in an array or something and make use of them later?
Here is my code:
rm(list = ls())
library(DT)
library(shiny)
library(shinyBS)
library(shinyjs)
library(shinydashboard)
shinyInput <- function(FUN, len, id, ...) {inputs <- character(len)
for (i in seq_len(len)) {
inputs[i] <- as.character(FUN(paste0(id, i), ...))}
inputs
}
ui <- dashboardPage(
dashboardHeader(title = "Simple App"),
dashboardSidebar(
sidebarMenu(id = "tabs",
menuItem("Menu Item 1", tabName = "one", icon = icon("dashboard"))
)
),
dashboardBody(
tabItems(
tabItem(tabName = "one",h2("Datatable Modal Popup"),
DT::dataTableOutput('my_table'),uiOutput("popup")
)
)
)
)
server <- function(input, output, session) {
my_data <- reactive({
testdata <- mtcars
as.data.frame(cbind(View = shinyInput(actionButton, nrow(testdata),'button_', label = "View", onclick = 'Shiny.onInputChange(\"select_button\", this.id)' ),
Check = shinyInput(checkboxInput,label = "Check", nrow(testdata), 'box_', value = FALSE),testdata))
})
output$my_table <- DT::renderDataTable(my_data(),selection = 'single',options = list(searching = FALSE,pageLength = 10),server = FALSE, escape = FALSE,rownames= FALSE)
SelectedRow <- eventReactive(input$select_button,{
as.numeric(strsplit(input$select_button, "_")[[1]][2])
})
observeEvent(input$select_button, {
toggleModal(session, "modalExample", "open")
})
DataRow <- eventReactive(input$select_button,{
my_data()[SelectedRow(),2:ncol(my_data())]
})
output$popup <- renderUI({
bsModal("modalExample", paste0("Data for Row Number: ",SelectedRow()), "", size = "large",
column(12,
DT::renderDataTable(DataRow())
)
)
})
}
shinyApp(ui, server)

Related

Display/plot filtered data (user selected) into newly created navbar tab in Shiny

I am currently facing a problem in Shiny where I am unable to display filtered data (user selected) into a newly created navbar tab. This had also led to another strange new tab removal problem.
Problem: I am stuck with the select data, appendtab (in navbar), outputUI and display/plot logic sequence in Shiny.
Scenario:
User selected data from local computer
User makes first selection from drop down list
Click on Add new tab
User makes second selection from drop down list
Click on Add new tab
Data used:
I don't know how to upload data on stackover flow but a simple csv table with two columns A and B will replicate the result below
Result:
Tab A: shows "Error: cannot coerce type 'closure' to vector of type 'character'"
Tab B: Delete tab function is now broken as well
My end goal to give more context: To be able to use this user selected data display charts, calcs, tables in the new tab.
What I did before it started erroring: I have followed similar logic to this post to display user filtered data in a new tab (not new navbartab though):
How to reuse a dataset in different objects when renderUI is used to create tabs in ShinyR
Also some help I got from Stackoverflow before this problem started. This may help with providing more context, all the answers from contributors worked:
Append and remove tabs using sidebarPanel
Can't get disable button to work with observeEvent with if statement in ShinyR
As always thank you very much for looking into my problem.
Cheers
library(shiny)
library(plyr)
library(dplyr)
library(DT)
library(shinyjs)
library(data.table)
ui <- fluidPage(
useShinyjs(),
navbarPage(title = "Stackoverflow help", id = "tabs",
tabPanel("Home",
sidebarPanel(
fileInput("file", "Upload data",
accept = c(
"text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
#checkboxInput("header", "Header", TRUE),
actionButton("append", "Add new tab"),
uiOutput('tabnamesui')
),
mainPanel(
)
)
)
)
server <- function(input, output, session) {
userfile <- reactive({
input$file
})
filereact <- reactive({
read.table(
file = userfile()$datapath,
sep = ',',
header = T,
stringsAsFactors = T
)
})
tabsnames <- reactive({
names(filereact())
})
output$tabnamesui <- renderUI({
req(userfile())
selectInput(
'tabnamesui',
h5('Tab names'),
choices = as.list(tabsnames()),
selected="",multiple = FALSE
)
})
tabnamesinput <- reactive({
input$tabnamesui})
#Delete selected tab logic
observeEvent(input$append,{
appendTab(inputId = "tabs",
tabPanel(input$tabnamesui,
sidebarPanel(
actionButton(paste0("remove_", input$tabnamesui), "Delete")),
mainPanel(
uiOutput("tabsets") #This is where I think something is broken
)
)
)
})
observeEvent(lapply(grep(pattern = "^remove_", x = names(input), value = TRUE), function(x){input[[x]]}),{
if(input$tabs != "Home"){
removeTab(inputId = "tabs", target = input$tabs)
updateSelectInput(session, "tabnamesui", selected = input$tabnamesui) # keep the selection when re-rendering sidebarPanel
}
})
#New tab logic to prevent inserting same tab twice with enable/disable action button
forcecombine = function(idtab,checker) {
colnames(idtab) = colnames(checker)
rbind(idtab,checker)
}
checker<-as.data.frame("checker")
idtab<-as.data.frame("checkers")
#only allow tab entry once
observeEvent(input$append, {
idtab <- paste0(tabnamesinput())
idtab<-as.data.frame(idtab)
checkerx<-forcecombine(idtab,checker)
repeated<-length(grep(idtab,checkerx))
if(repeated==1)
{
shinyjs::disable("append")
}
else {shinyjs::enable("append")
}
})
observeEvent(tabnamesinput(), {
shinyjs::enable("append")
})
shinyjs::disable("append")
observeEvent(input$file, {
shinyjs::enable("append")
})
#Subdata section, I want to only use the data the user has selected for the new Navbar tab
output$tabsets<-renderUI({
req(userfile())
tabtable<-reactive({
lapply(tabnamesinput(), function(x)
dataTableOutput(paste0('table',x)))
})
})
subsetdata<-reactive({
list_of_subdata<-lapply(tabnamesinput(), function(x) {
as.data.table((select(filereact(),x)))
})
names(list_of_subdata)<-tabnamesinput()
return(list_of_subdata)
})
observe(
lapply(tabnamesinput(), function(x) {
output[[paste0('table',x)]] <-
renderDataTable({
subsetdata()[[x]]
})}))
}
shinyApp(ui, server)
You cannot output same ID in multiple tabs. Once you fix that, it works. You still need to define what you wish to display in each tab. I am just displaying a filtered table and a sample plot. Also, tab removal required minor tweak. Working code is shown below.
ui <- fluidPage(
useShinyjs(),
navbarPage(title = "Stackoverflow help", id = "tabs",
tabPanel("Home",
sidebarPanel(
fileInput("file", "Upload data",
accept = c(
"text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
#checkboxInput("header", "Header", TRUE),
actionButton("append", "Add new tab"),
uiOutput('tabnamesui')
),
mainPanel(
)
)
)
)
server <- function(input, output, session) {
userfile <- reactive({
input$file
})
filereact <- reactive({
read.table(
file = userfile()$datapath,
sep = ',',
header = T,
stringsAsFactors = T
)
})
tabsnames <- reactive({
names(filereact())
})
output$tabnamesui <- renderUI({
req(userfile())
selectInput(
'tabnamesui',
h5('Tab names'),
choices = as.list(tabsnames()),
selected="",multiple = FALSE
)
})
tabnamesinput <- reactive({
input$tabnamesui})
#Append selected tab logic
observeEvent(input$append,{
appendTab(inputId = "tabs",
tabPanel(input$tabnamesui,
sidebarPanel(
actionButton(paste0("remove_", input$tabnamesui), "Delete")),
mainPanel(
#uiOutput("tabsets") #This is where I think something is broken
DTOutput(paste0("table",input$tabnamesui)),
plotOutput(paste0("plot",input$tabnamesui))
)
)
)
})
# Delete selected tab logic
observeEvent(lapply(grep(pattern = "^remove_", x = names(input), value = TRUE), function(x){input[[x]]}),{
if(input$tabs != "Home"){
if (input[[paste0("remove_",input$tabs)]]) { ## remove tab only if delete button has been clicked
removeTab(inputId = "tabs", target = input$tabs)
updateSelectInput(session, "tabnamesui", selected = input$tabnamesui) # keep the selection when re-rendering sidebarPanel
}
}
})
#New tab logic to prevent inserting same tab twice with enable/disable action button
forcecombine = function(idtab,checker) {
colnames(idtab) = colnames(checker)
rbind(idtab,checker)
}
checker<-as.data.frame("checker")
idtab<-as.data.frame("checkers")
#only allow tab entry once
observeEvent(input$append, {
idtab <- paste0(tabnamesinput())
idtab<-as.data.frame(idtab)
checkerx<-forcecombine(idtab,checker)
repeated<-length(grep(idtab,checkerx))
if(repeated==1)
{
shinyjs::disable("append")
}
else {shinyjs::enable("append")
}
})
observeEvent(input$tabnamesui, {
shinyjs::enable("append")
output[[paste0("plot",input$tabnamesui)]] <- renderPlot(plot(cars))
lapply(tabnamesinput(), function(x) {
df <- as.data.table(filereact()[[as.name(tabnamesinput())]])
output[[paste0('table',x)]] <- renderDT({
df
#subsetdata()[[x]]
})})
})
shinyjs::disable("append")
observeEvent(input$file, {
shinyjs::enable("append")
})
}
shinyApp(ui, server)
Try this:
library(plotly)
library(shiny)
library(DT)
ui <- fluidPage(
mainPanel(
plotlyOutput("SepalPlot"),
DT::dataTableOutput("Sepal"),
plotlyOutput("PetalPlot"),
DT::dataTableOutput("Petal")
)
)
server <- function(input, output) {
output$SepalPlot<- renderPlotly({
plot_ly(iris, x = ~Sepal.Length, y = ~Sepal.Width, type = 'scatter', mode = 'markers')
})
sep<-data.frame(c(iris$Sepal.Length, iris$Sepal.Width))
output$Sepal<-renderDataTable({datatable(sep)})
output$PetalPlot<- renderPlotly({
plot_ly(iris, x = ~Petal.Length, y = ~Petal.Width, type = 'scatter', mode = 'markers')
})
pet<-data.frame(c(iris$Petal.Length, iris$Petal.Width))
output$Petal<-renderDataTable({pet})
}
shinyApp(ui = ui, server = server)

Having issues appending tab on cell click within observeEvent function in R Shiny

This is the minimum reproducible example needed to help:
ui.R
library(shiny)
fluidPage(
title = 'DataTables Information',
tabsetPanel(id = "tabs",
tabPanel("Cars overview",
h1("Cars overview"),
div("Click any cell"),
br(),
DT::dataTableOutput("x4")
)
)
)
Server script:
server.R
library(shiny)
library(DT)
shinyServer(function(input, output, session) {
output$x4 = DT::renderDataTable({
DT::datatable(mtcars, selection = 'single')
}, server = TRUE)
observeEvent(input$x4_cells_clicked, {
print("Trigger")
value <- x4_cells_clicked$value
details <- mtcars %>%
filter(mpg == value)
appendTab(inputId = "tabs",
tabPanel(
DT::renderDataTable(DT::datatable(details), server = TRUE)
)
)
# Focus on newly created tab
updateTabsetPanel(session, "tabs", selected = "Car details")
})
})
What I am trying to accomplish is to trigger an event through a cell click on the mtcars dataframe. I want to append a tab upon a click and filter the dataframe that is produced by the value within the cell that is clicked. I know in this case I am only accounting for a click on the mpg column but I just need to see how a click on a cell is registered through observeEvent and how to use the value of the cell clicked to filter the dataframe that is produced in the new tab.
library(shiny)
library(DT)
ui <- fluidPage(
title = 'DataTables Information',
tabsetPanel(id = "tabs",
tabPanel("Cars overview",
h1("Cars overview"),
div("Click any cell"),
br(),
DTOutput("x4")
)
)
)
server <- function(input, output, session) {
output$x4 = renderDT({
datatable(mtcars, selection = 'single')
}, server = TRUE)
observeEvent(input$x4_cell_clicked, {
cell <- input$x4_cell_clicked
if(length(cell)){
details <- mtcars[mtcars[[cell$col]]==cell$value,]
appendTab(inputId = "tabs",
tabPanel(
"Cars details",
renderDT(datatable(details), server = TRUE)
),
select = TRUE # Focus on newly created tab
)
}
})
}
shinyApp(ui, server)

Refreshing Filter and Table

I have the following code:
library(shiny)
library(shinydashboard)
library(rhandsontable)
header <- dashboardHeader(title = "Sample", titleWidth = 375)
sidebar <- dashboardSidebar(width = 270,
sidebarMenu(id="mymenu",
menuItem(text = "Home", tabName = "tabCars", icon = icon("home", class="home"))
))
body <- dashboardBody (
tabItems(
tabItem(tabName = "tabCars",
fluidRow(
column(width = 2,
selectInput(
inputId = "selected_CarCylinders",
label = "Car Cylinders",
choices = mtcars$cyl,
selectize = TRUE,
width = "250px",
multiple = FALSE
)),
column(width = 2, style = "margin-top: 25px",
actionButton("deleteBtn", "Delete Selected Cylinders")),
column(width = 1, style = "margin-top: 25px",
actionButton("refreshBtn", "Refresh Filter/Chart")),
rHandsontableOutput("carDT")
)
)
)
)
ui <- dashboardPage(header, sidebar, body)
server <- function(input, output, session) {
output$carDT <- renderRHandsontable({
df <- mtcars
rhandsontable(df, stretchH = "all")
})
observeEvent(input$deleteBtn, {
# need help here
})
observeEvent(input$refreshBtn, {
# need help here
})
}
shinyApp(ui, server)
I need help writing what would go into the input$deleteBtn and input$refreshBtn sections of the server side. If you run the code as is, the idea is to select the number of cylinders from mtcars, then click the Delete button to remove all those entries from the table and filter; however, the filter and table would only update after clicking the refresh button.
While permanently delete screams a SQLite database to me, you could achieve this by using a reactiveVal to store the dataframe and call req to only refresh the table when you click the refreshBtn (in this case, you also have to click it to display the table at the start of the app).
server <- function(input, output, session) {
# Create a `reactiveVal` and set a value to it
df <- reactiveVal()
df(mtcars)
output$carDT <- renderRHandsontable({
req(input$refreshBtn)
rhandsontable(df(), stretchH = "all")
})
observeEvent(input$deleteBtn, {
data <- dplyr::filter(df(), cyl != input$selected_CarCylinders)
# Update `selectInput` to filter out the choices too (for good measure)
updateSelectInput(session, "selected_CarCylinders", choices = data$cyl)
# Update the `reactiveVal` value
df(data)
})
}

How to get the selected rows from DT within the popup modal (user action)

I used ShinyBS package to make a popup modal. When I hit view, it pops up a window. Inside of the popup modal is a data table made by the DT package. I wanted to select rows and display the ID number of the row I selected at the bottom of the popup window. However, I don't know what's the correct "Input" name to get it.
The following is my sample code.
#rm(list = ls())
library(DT)
library(shiny)
library(shinyBS)
library(shinyjs)
library(shinydashboard)
# This function will create the buttons for the datatable, they will be unique
shinyInput <- function(FUN, len, id, ...) {inputs <- character(len)
for (i in seq_len(len)) {
inputs[i] <- as.character(FUN(paste0(id, i), ...))}
inputs
}
ui <- dashboardPage(
dashboardHeader(title = "Simple App"),
dashboardSidebar(
sidebarMenu(id = "tabs",
menuItem("Menu Item 1", tabName = "one", icon = icon("dashboard"))
)
),
dashboardBody(
tabItems(
tabItem(tabName = "one",h2("Datatable Modal Popup"),
DT::dataTableOutput('my_table'),uiOutput("popup")
)
)
)
)
server <- function(input, output, session) {
my_data <- reactive({
testdata <- cars
as.data.frame(
cbind(
View = shinyInput(actionButton,
nrow(testdata),
'button_',
label = "View",
onclick = 'Shiny.onInputChange(\"select_button\", this.id)' ),
testdata))
})
output$my_table <- DT::renderDataTable(my_data(),selection = 'single',options = list(searching = FALSE,pageLength = 10),server = FALSE, escape = FALSE,rownames= FALSE)
SelectedRow <- eventReactive(input$select_button,{
as.numeric(strsplit(input$select_button, "_")[[1]][2])
})
observeEvent(input$select_button, {
toggleModal(session, "modalExample", "open")
})
DataRow <- eventReactive(input$select_button,{
iris
})
## I guess my input name is not right
output$y11 = renderPrint(input$popup_rows_selected)
output$popup <- renderUI({
bsModal("modalExample", paste0("Data for Row Number: ",SelectedRow()), "", size = "large",
column(12,
DT::renderDataTable(DataRow()),
h4("The following didn't show when I select the rows"),
verbatimTextOutput('y11')
)
)
})
}
shinyApp(ui, server)
This will work, the event has to be bound to the table id, which you had it for ui element(can contain multiple things)
#rm(list = ls())
library(DT)
library(shiny)
library(shinyBS)
library(shinyjs)
library(shinydashboard)
# This function will create the buttons for the datatable, they will be unique
shinyInput <- function(FUN, len, id, ...) {inputs <- character(len)
for (i in seq_len(len)) {
inputs[i] <- as.character(FUN(paste0(id, i), ...))}
inputs
}
ui <- dashboardPage(
dashboardHeader(title = "Simple App"),
dashboardSidebar(
sidebarMenu(id = "tabs",
menuItem("Menu Item 1", tabName = "one", icon = icon("dashboard"))
)
),
dashboardBody(
tabItems(
tabItem(tabName = "one",h2("Datatable Modal Popup"),
DT::dataTableOutput('my_table'),uiOutput("popup")
)
)
)
)
server <- function(input, output, session) {
my_data <- reactive({
testdata <- cars
as.data.frame(
cbind(
View = shinyInput(actionButton,
nrow(testdata),
'button_',
label = "View",
onclick = 'Shiny.onInputChange(\"select_button\", this.id)' ),
testdata))
})
output$my_table <- DT::renderDataTable(my_data(),selection = 'single',options = list(searching = FALSE,pageLength = 10),server = FALSE, escape = FALSE,rownames= FALSE)
SelectedRow <- eventReactive(input$select_button,{
as.numeric(strsplit(input$select_button, "_")[[1]][2])
})
observeEvent(input$select_button, {
toggleModal(session, "modalExample", "open")
})
DataRow <- eventReactive(input$select_button,{
iris
})
## I guess my input name is not right
output$y11 = renderPrint(input$my_test_rows_selected)
output$my_test <- DT::renderDataTable(DataRow())
output$popup <- renderUI({
bsModal("modalExample", paste0("Data for Row Number: ",SelectedRow()), "", size = "large",
column(12,
dataTableOutput("my_test"),
h4("The following didn't show when I select the rows"),
verbatimTextOutput('y11')
)
)
})
}
shinyApp(ui, server)

Shiny - Can dynamically generated buttons act as trigger for an event

I have a shiny code that generates actions buttons from a numericInput and each of those actions buttons generate a plot when clicked using a observeEvent. The problem is that I don't know how to trigger an event with dynamically generated buttons. The workaround I used was to make a observeEvent for each button but if I generate more buttons than the obserEvents I created it won't work.
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(title = "Dynamic selectInput"),
dashboardSidebar(
sidebarMenu(
menuItemOutput("menuitem")
)
),
dashboardBody(
numericInput("go_btns_quant","Number of GO buttons",value = 1,min = 1,max = 10),
uiOutput("go_buttons"),
plotOutput("plot")
)
)
server <- function(input, output, session) {
output$menuitem <- renderMenu({
menuItem("Menu item", icon = icon("calendar"))
})
output$go_buttons <- renderUI({
buttons <- as.list(1:input$go_btns_quant)
buttons <- lapply(buttons, function(i)
fluidRow(
actionButton(paste0("go_btn",i),paste("Go",i))
)
)
})
#Can this observeEvents be triggerd dynamicly?
observeEvent(input[[paste0("go_btn",1)]],{output$plot <-renderPlot({hist(rnorm(100,4,1),breaks = 10)})})
observeEvent(input[[paste0("go_btn",2)]],{output$plot <- renderPlot({hist(rnorm(100,4,1),breaks = 50)})})
observeEvent(input[[paste0("go_btn",3)]],{output$plot <- renderPlot({hist(rnorm(100,4,1),breaks = 100)})})
observeEvent(input[[paste0("go_btn",4)]],{output$plot <- renderPlot({hist(rnorm(100,4,1),breaks = 200)})})
observeEvent(input[[paste0("go_btn",5)]],{output$plot <- renderPlot({hist(rnorm(100,4,1),breaks = 500)})})
}
shinyApp(ui, server)
You can also create observers dynamically. Just make sure that they are created only once, otherwise they will execute several times.
Below is your code modified to create as many observers as buttons. Please note that if an observer for the button already exist, it should not be created. You can customize your observers too, so each observer could have its own behavior.
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(title = "Dynamic selectInput"),
dashboardSidebar(
sidebarMenu(
menuItemOutput("menuitem")
)
),
dashboardBody(
numericInput("go_btns_quant","Number of GO buttons",value = 1,min = 1,max = 10),
uiOutput("go_buttons"),
plotOutput("plot")
)
)
server <- function(input, output, session) {
output$menuitem <- renderMenu({
menuItem("Menu item", icon = icon("calendar"))
})
# to store observers and make sure only once is created per button
obsList <- list()
output$go_buttons <- renderUI({
buttons <- as.list(1:input$go_btns_quant)
buttons <- lapply(buttons, function(i)
{
btName <- paste0("go_btn",i)
# creates an observer only if it doesn't already exists
if (is.null(obsList[[btName]])) {
# make sure to use <<- to update global variable obsList
obsList[[btName]] <<- observeEvent(input[[btName]], {
cat("Button ", i, "\n")
output$plot <-renderPlot({hist(rnorm(100, 4, 1),breaks = 50*i)})
})
}
fluidRow(
actionButton(btName,paste("Go",i))
)
}
)
})
}

Resources