Unique sidebar inputs for each new dynamic tab created in Shiny - r

I would like to have unique user inputs for each newly created tab in Shiny, however once the user selects the inputs it stores and does not change for the additional tabs created.
Scenario:
User selected data from local computer
User makes selection from drop down list
Click on Add new tab
Click on the new tab
User custom input = graph changes dynamically
Go back to homepage select new data and Click on Add new tab
Click on the new tab
User custom input = graph does not change and changes as per user input from step 5
Data: Any simple csv table with two columns A and B will replicate the result below
Desired result: Each tab has unique user input and changes the active tab graph dynamically
Section of code where I think the problem is: At lines 68 and 120. Is there a way to set unique inputs for each ammended tab?
Thanks for looking into my problem.
library(shiny)
library(plyr)
library(dplyr)
library(DT)
library(shinyjs)
library(data.table)
library(ggplot2)
ui <- fluidPage(
useShinyjs(),
navbarPage(title = "Test", 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"),
textInput("x", "X-axis label"),
textInput("titlename", "Title"),
sliderInput("bins", "Number of bins", value = 50, min = 1, max = 100)
),
mainPanel(
plotOutput(paste0("dp2",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")
lapply(tabnamesinput(), function(x) {
df <- as.data.table(filereact()[[as.name(tabnamesinput())]])
output[[paste0("dp2",input$tabnamesui)]] <- renderPlot({
bins <- seq(min(as.numeric(unlist(df))), max(as.numeric(unlist(df))), length.out = input$bins + 1)
hist(as.numeric(unlist(df)), # histogram
col="gray",
xlim=c(min(as.numeric(unlist(df))), max(as.numeric(unlist(df)))),
border="black",
breaks = seq(min(as.numeric(unlist(df))), max(as.numeric(unlist(df))), length.out = input$bins+1),
prob = TRUE, # show densities instead of frequencies
xlab = input$x,
main = input$titlename)
})
})
})
shinyjs::disable("append")
observeEvent(input$file, {
shinyjs::enable("append")
})
}
shinyApp(ui, server)

Try this
ui <- fluidPage(
useShinyjs(),
navbarPage(title = "Test", 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"),
textInput(paste0("x.",input$tabnamesui), "X-axis label"),
textInput(paste0("titlename",input$tabnamesui), "Title"),
sliderInput("bins", "Number of bins", value = 50, min = 1, max = 100)
),
mainPanel(
plotOutput(paste0("dp2",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")
lapply(tabnamesinput(), function(x) {
df <- as.data.table(filereact()[[as.name(tabnamesinput())]])
tab_name <- input$tabnamesui
output[[paste0("dp2",input$tabnamesui)]] <- renderPlot({
bins <- seq(min(as.numeric(unlist(df))), max(as.numeric(unlist(df))), length.out = input$bins + 1)
hist(as.numeric(unlist(df)), # histogram
col="gray",
xlim=c(min(as.numeric(unlist(df))), max(as.numeric(unlist(df)))),
border="black",
breaks = seq(min(as.numeric(unlist(df))), max(as.numeric(unlist(df))), length.out = input$bins+1),
prob = TRUE, # show densities instead of frequencies
xlab = input[[paste0("x.",tab_name)]],
main = input[[paste0("titlename",tab_name)]] )
})
})
})
shinyjs::disable("append")
observeEvent(input$file, {
shinyjs::enable("append")
})
}
shinyApp(ui, server)

Related

Delete map after pressing reset button on shiny

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.

How to fetch the dynamic slider values in r shiny app?

I stuck in printing dynamic slider values. In the following code I tried to print the dynamic slider values but it's not possible.
library(shinydashboard)
library(DT)
ui <- dashboardPage(
dashboardHeader(title = "Dynamic slider"),
dashboardSidebar(
tags$head(
tags$style(HTML('.skin-blue .main-sidebar {
background-color: #666666;
}'))
),
sidebarMenu(
menuItem("Input data", tabName = 'input_data')
),
fileInput(
"file",
"Choose CSV File",
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
checkboxInput("header",
"Header",
value = TRUE),
radioButtons(
"disp",
"Display",
choices = c(Head = "head",
All = "all"),
selected = "head"
),
sliderInput(
inputId = 'slr',
label = 'Slider range',
min = 0,
max = 3,
value = c(0.5,3),
step = 0.5
),
selectInput(
inputId = 'var',
label = 'Variables',
'Names',
multiple = TRUE
),
uiOutput('sliders')
),
dashboardBody(tabItems(
tabItem(tabName = 'input_data',
fluidRow(
box(width = 12,
dataTableOutput('table'),
title = 'Raw data'),
box(width = 6,
verbatimTextOutput('slider1'),
title = 'slider range'),
box(width = 6,
verbatimTextOutput('slider2'),
title = 'dynamic slider value')
)
)
))
)
server <- function(input, output) {
dataset <- reactive({
req(input$file)
read.csv(input$file$datapath,header = input$header)
})
observe(
output$table <- DT::renderDataTable({
if (input$disp == 'head') {
head(dataset())
}
else{
dataset()
}
})
)
observe({
updateSelectInput(inputId = 'var',choices = c(' ',names(dataset())))
})
variables <- reactive({
input$var
})
sli <- reactive({
lapply(1:length(variables()), function(i){
inputName <- variables()[i]
sliderInput(inputName, inputName,
min = 0, max = 1, value = c(0.3,0.7))
})
})
output$sliders <- renderUI({
do.call(tagList,sli())
})
output$slider1 <- renderPrint({
input$slr
})
output$slider2 <- renderPrint({
sli()
})
}
shinyApp(ui = ui, server = server)
Any suggestions will be appreciated, Is there any other method to get dynamic sliders based on selected variables or How can we get the values of the dynamic slider here??
There may be better ways to structure your app, but here is a solution that follows your general approach. There are 4 modifications to what you already have:
There is no need to define the reactive variables when you can just use input$var directly. The proposed solution eliminates this reactive.
Using req(input$var) will prevent components dependent on that selectInput from trying to render when a selection has not been made.
Since input$var defines the id of the dynamic slider, you can use this to retrieve the slider's values (i.e., input[[input$var]]).
Since you have specified "multiple = TRUE", a few nested paste statements are used to create a single string representing the values of all (potentially multiple) dynamic sliders.
The below app includes these modifications, and I believe, achieves what you are trying to accomplish.
library(shinydashboard)
library(DT)
ui <- dashboardPage(
dashboardHeader(title = "Dynamic slider"),
dashboardSidebar(
tags$head(
tags$style(HTML('.skin-blue .main-sidebar {
background-color: #666666;
}'))
),
sidebarMenu(
menuItem("Input data", tabName = 'input_data')
),
fileInput(
"file",
"Choose CSV File",
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
checkboxInput("header",
"Header",
value = TRUE),
radioButtons(
"disp",
"Display",
choices = c(Head = "head",
All = "all"),
selected = "head"
),
sliderInput(
inputId = 'slr',
label = 'Slider range',
min = 0,
max = 3,
value = c(0.5,3),
step = 0.5
),
selectInput(
inputId = 'var',
label = 'Variables',
'Names',
multiple = TRUE
),
uiOutput('sliders')
),
dashboardBody(tabItems(
tabItem(tabName = 'input_data',
fluidRow(
box(width = 12,
dataTableOutput('table'),
title = 'Raw data'),
box(width = 6,
verbatimTextOutput('slider1'),
title = 'slider range'),
box(width = 6,
verbatimTextOutput('slider2'),
title = 'dynamic slider value')
)
)
))
)
server <- function(input, output) {
dataset <- reactive({
req(input$file)
read.csv(input$file$datapath,header = input$header)
})
observe(
output$table <- DT::renderDataTable({
if (input$disp == 'head') {
head(dataset())
}
else{
dataset()
}
})
)
observe({
updateSelectInput(inputId = 'var',choices = c(' ',names(dataset())))
})
sli <- reactive({
lapply(1:length(input$var), function(i){
inputName <- input$var[i]
sliderInput(inputName, inputName,
min = 0, max = 1, value = c(0.3,0.7))
})
})
output$sliders <- renderUI({
req(input$var)
do.call(tagList,sli())
})
output$slider1 <- renderPrint({
input$slr
})
output$slider2 <- renderPrint({
req(input$var)
paste(
sapply(
input$var,
function(x) {
paste(x, paste(input[[x]], collapse = ', '), sep = ': ')
}
),
collapse = '; '
)
})
}
shinyApp(ui = ui, server = server)

How to create a button that will create a pdf file of a table

I currently have a table being generated and I would like the user to be able to create a pdf file when they click the download button.
I am currently getting an error where when I click the download button I get an html file that downloads the entire page of the app. I thought that using pdf(file) would work but it ignores the function.
Here is currently what I have.
library(shiny)
library(xlsx)
library(shinyWidgets)
population <- read.xlsx("population.xlsx", 1)
fieldsMandatory <- c("selectedCountry")
labelMandatory <- function(label) {
tagList(
label,
span("*", class = "mandatory_star")
)
}
appCSS <-
".mandatory_star {color: red;}"
ui <- fluidPage(
navbarPage(title = span("Spatial Tracking of COVID-19 using Mathematical Models", style = "color:#000000; font-weight:bold; font-size:15pt"),
tabPanel(title = "Model",
sidebarLayout(
sidebarPanel(
shinyjs::useShinyjs(),
shinyjs::inlineCSS(appCSS),
div(
id = "dashboard",
pickerInput(
inputId = "selectedCountry",
labelMandatory ("Country"),
choices = population$Country,
multiple = FALSE,
options = pickerOptions(
actionsBox = TRUE,
title = "Please select a country")
),
sliderInput(inputId = "agg",
label = "Aggregation Factor",
min = 0, max = 50, step = 5, value = 10),
actionButton("go","Run Simulation"),
)
),
mainPanel(
tabsetPanel(
tabPanel("Input Summary", verbatimTextOutput("summary"),
tableOutput("table"),
downloadButton(outputId = "downloadSummary", label = "Save Summary"))
)
)
)
)
)
)
server <- function(input, output, session){
observeEvent(input$resetAll, {
shinyjs::reset("dashboard")
})
values <- reactiveValues()
values$df <- data.frame(Variable = character(), Value = character())
observeEvent(input$go, {
row1 <- data.frame(Variable = "Country", Value = input$selectedCountry)
row2 <- data.frame(Variable = "Aggregation Factor", Value = input$agg)
values$df <- rbind(row1, row2)
})
output$table <- renderTable(values$df)
observe({
# check if all mandatory fields have a value
mandatoryFilled <-
vapply(fieldsMandatory,
function(x) {
!is.null(input[[x]]) && input[[x]] != ""
},
logical(1))
mandatoryFilled <- all(mandatoryFilled)
# enable/disable the submit button
shinyjs::toggleState(id = "go", condition = mandatoryFilled)
})
output$downloadSummary <- downloadHandler(
filename = function(file) {
paste('my-report.pdf', )
},
content = function(file) {
pdf(file)
}
)
}
shinyApp(ui,server)
Here's a minimal example:
library(shiny)
ui <- fluidPage(
downloadButton("savepdf", "Save pdf")
)
server <- function(input, output, session) {
output$savepdf <- downloadHandler(
filename = "test.pdf",
content = function(file) {
pdf(file)
plot(iris$Sepal.Length, iris$Sepal.Width)
dev.off()
}
)
}
shinyApp(ui, server)
Also see here.
Here is a minimal example with the package latexpdf. It will create the pdf table in the folder of the app.
library(shiny)
library(latexpdf)
dat <- head(iris, 5)
ui <- fluidPage(
br(),
actionButton("dwnld", "Create pdf"),
tableOutput("mytable")
)
server <- function(input, output, session){
output[["mytable"]] <- renderTable({
dat
})
observeEvent(input[["dwnld"]], {
as.pdf(dat)
})
}
shinyApp(ui, server)

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)

Use insertUI to create different kind of shiny widgets

I have the shiny app below in which the user may select between one or more column names from the data frame.
name<-c("John","Jack","Bill")
value1<-c(2,4,6)
add<-c("SDF","GHK","FGH")
value2<-c(3,4,5)
dt<-data.frame(name,value1,add,value2)
Then for every selection he makes the relative pickerInput() may be displayed below. The issue is that I would like to set a diffrent widget for specific columns. Let's say that I would like to set those with numeric values as sliderInput(). I have 2 versions below of the same app. So if any solution is applicable to on of them it would be ok.
app1
library(shiny)
library(shinyWidgets)
library(DT)
# ui object
ui <- fluidPage(
titlePanel(p("Spatial app", style = "color:#3474A7")),
sidebarLayout(
sidebarPanel(
pickerInput(
inputId = "p1",
label = "Select Column headers",
choices = colnames( dt),
multiple = TRUE,
options = list(`actions-box` = TRUE)
),
#Add the output for new pickers
uiOutput("pickers")
),
mainPanel(
)
)
)
# server()
server <- function(input, output) {
observeEvent(input$p1, {
#Create the new pickers
output$pickers<-renderUI({
div(lapply(input$p1, function(x){
pickerInput(
inputId = x#The colname of selected column
,
label = x #The colname of selected column
,
choices = dt[,x]#all rows of selected column
,
multiple = TRUE,
options = list(`actions-box` = TRUE)
)
}))
})
})
}
# shinyApp()
shinyApp(ui = ui, server = server)
app2
library(shiny)
library(shinyWidgets)
library(DT)
name<-c("John","Jack","Bill")
value1<-c(2,4,6)
add<-c("SDF","GHK","FGH")
value2<-c(3,4,5)
dt<-data.frame(name,value1,add,value2)
# ui object
ui <- fluidPage(
titlePanel(p("Spatial app", style = "color:#3474A7")),
sidebarLayout(
sidebarPanel(
pickerInput(
inputId = "p1",
label = "Select Column headers",
choices = colnames( dt),
multiple = TRUE,
options = list(`actions-box` = TRUE)
),
tags$div(id = "add_ui_here")
),
mainPanel(
)
)
)
# server()
server <- function(input, output) {
# store currently selected columns
selected_columns <- c()
observeEvent(input$p1, {
# determine pickerInputs to remove
input_remove <- !selected_columns %in% input$p1
input_remove <- selected_columns[input_remove]
# remove inputs
if (!is.null(input_remove) && length(input_remove) > 0) {
for (input_element in input_remove) {
removeUI(selector = paste0("#", input_element, "_remove_id"))
}
}
# determine pickerInputs to add
input_add <- !input$p1 %in% selected_columns
input_add <- input$p1[input_add]
# add inputs
if (length(input_add) > 0) {
for (input_element in input_add) {
insertUI(
selector = "#add_ui_here",
where = "afterEnd",
ui = tags$div(id = paste0(input_element, "_remove_id"),
pickerInput(
inputId = input_element
,
label = input_element
,
choices = dt[, input_element]
,
multiple = TRUE,
options = list(`actions-box` = TRUE)
))
)
}
}
# update the currently stored column variable
selected_columns <<- input$p1
},
ignoreNULL = FALSE)
}
# shinyApp()
shinyApp(ui = ui, server = server)
Check if the selected variable is numeric or character and assign the appropriate widget. Try this code.
name<-c("John","Jack","Bill")
value1<-c(2,4,6)
add<-c("SDF","GHK","FGH")
value2<-c(3,4,5)
dt<-data.frame(name,value1,add,value2)
# ui object
ui <- fluidPage(
titlePanel(p("Spatial app", style = "color:#3474A7")),
sidebarLayout(
sidebarPanel(
pickerInput(
inputId = "p1",
label = "Select Column headers",
choices = colnames( dt),
multiple = TRUE,
options = list(`actions-box` = TRUE)
),
#Add the output for new pickers
uiOutput("pickers")
),
mainPanel(
)
)
)
# server()
server <- function(input, output) {
observeEvent(input$p1, {
#Create the new pickers
output$pickers<-renderUI({
div(lapply(input$p1, function(x){
if (is.numeric(dt[[x]])) {
sliderInput(inputId=x, label=x, min=min(dt[x]), max=max(dt[[x]]), value=min(dt[[x]]))
}
else if (is.character(dt[[x]])) {
pickerInput(
inputId = x#The colname of selected column
,
label = x #The colname of selected column
,
choices = dt[,x]#all rows of selected column
,
multiple = TRUE,
options = list(`actions-box` = TRUE)
)
}
}))
})
})
}
# shinyApp()
shinyApp(ui = ui, server = server)

Resources