I have what could appear as a very simple problem. I'd like to display different plots in different tabs. For this I have some code already where I use output$Sidebar and output$TABUI for the content of the tabs.
I do wish to use some controls for the plots, but all the controls being identical, I need them just below the different tabs, as I don't want to replicate them across and having them appearing within each tab.
I must miss something in my code because nothing show up in the dashboardbody. The tabs are created just fine (as it seems) so are my controls, just below them. My data is read through (can see this in the console) and I can work with the controls, but nothing appears in the body.
I've tried to modify my code (much longer) to make a minimal example, as follow.
Edit : If both the sidebarmenu and tabitems are in the UI.R, then everything on the ui gets compiled correctly, except that my data, which are being loaded at the beginning of the SERVER.R are not loaded. It seems as if server.R is not even ran.
If I define the sidebarmenu and thetabitems from the server.R, then the data are loaded, but only my controls are displayed, sidebarmenu and body are not displayed. I can't understand this behavior either.
If I leave tabitems in the UI.R and sidebarmenu from server.R, it does not load the data either. The app just seats there and nothing happens.
If someone think they might know why, I'd be glad to have an explanation.
Thank you.
ui.R :
library(shiny)
library(shinydashboard)
body <- dashboardBody(
tags$head(
tags$link(
rel = "stylesheet",
type = "text/css",
href = "css/custom.css"
)
),
uiOutput("TABUI")
)
sidebar <- dashboardSidebar(
width = 350,
uiOutput("Sidebar")
)
header <- dashboardHeader(
title = "Dashboard",
titleWidth = 350,
tags$li(
class = "dropdown",
img(
src = 'img/General_Logo.png',
style = 'margin-right:150px; margin-top:21px')
)
)
dashboardPage(
header,
sidebar,
body
)
Server.R
library(ggplot2)
library(dplyr)
library(RColorBrewer)
library(XLConnect)
library(htmlTable)
library(plotly)
# Loading data -----------------------------------------------------
raw_data <- read.csv("file.csv")
# Server function ---------------------------------------------------
shinyServer(function(input, output) {
# Tabs and content
ntabs <- 4
tabnames <- paste0("tab ", 1:ntabs)
output$Sidebar <- renderUI({
Menus <- vector("list", ntabs + 2)
for (i in 1:ntabs){
Menus[[i]] <- menuItem(tabnames[i], tabName = tabnames[i], icon = icon("dashboard"))
}
# Controls to appear below tabs
Menus[[ntabs + 1]] <- selectInput("dpt", "Departments :",
c("dpt 1" = "DPT1",
"dpt 2" = "DPT2",
"dpt 3" = "DPT3"),
multiple = TRUE,
selectize = TRUE)
Menus[[ntabs + 2]] <- uiOutput("bottleneck")
Menus[[ntabs + 3]] <- uiOutput("daterange")
Menus[[ntabs + 4]] <- submitButton()
do.call(function(...) sidebarMenu(id = 'sidebarMenu', ...), Menus)
})
# content of each tab
output$TABUI <- renderUI({
Tabs <- vector("list", ntabs)
Tabs[[1]] <- tabItem(tabName = tabnames[1],
# fluidRow(box(h3("foo.")))
fluidRow(
box(
plotOutput("plot_1")
)
)
)
Tabs[[2]] <- tabItem(tabName = tabnames[2],
"Tab 2 Stuff")
Tabs[[3]] <- tabItem(tabName = tabnames[3],
"Tab 3 Stuff")
Tabs[[4]] <- tabItem(tabName = tabnames[4],
"Tab 4 Stuff")
do.call(tabItems, Tabs)
})
formulaText <- reactive({
if (is.null(data.r())) {
return("some text")
}
paste0(as.character(input$daterange[1]), " to ", as.character(input$daterange[2]), " - blah blah")
})
output$bottleneck <- renderUI({
selectInput('bottleneck', HTML('<font color=\"black\"> Bottlenecks : </font>'), c(Choose = '', raw_data[raw_data$is_bottleneck == 1 & !is.na(raw_data$Sort.field) & raw_data$Cost.Center %in% input$dpt,]$Sort.field %>% unique() %>% sort()), selectize = TRUE)
})
output$daterange <- renderUI({
dateRangeInput(inputId = 'daterange',
label = HTML('<font color=\"black\"> Select period : </font>'),
min = min(raw_data$Completn.date) ,
start = min(raw_data$Completn.date) ,
max = max(raw_data$Completn.date),
end = max(raw_data$Completn.date))
})
data.r = reactive({
if (is.null(input$dpt)) {
return(NULL)
}
ifelse(input$bottleneck == "", a <- raw_data %>% filter(Completn.date >= input$daterange[1],
Completn.date <= input$daterange[2]),
a <- raw_data %>% filter(Completn.date >= input$daterange[1],
Completn.date <= input$daterange[2],
Sort.field %in% input$bottleneck))
return(a)
})
output$table_ranking <- renderHtmlTableWidget({
if (is.null(data.r())) {
return()
}
ranking <- read.csv("ranking.csv", header = TRUE)
htmlTableWidget(ranking)
})
output$caption <- renderText({
formulaText()
})
output$plot_1 <- renderPlot({
if (is.null(data.r())) {
return()
}
current_data <- data.r()
p0 <- current_data %>%
ggplot(aes(x = x1, y = y1)) +
geom_point()
p0
})
output$plot_2 <- renderPlot({
if (is.null(data.r())) {
return()
}
current_data <- data.r()
p0 <- current_data %>%
ggplot(aes(x = x2, y = y2)) +
geom_point()
p0
})
})
This is a failed attempt to replicate what was suggested here.
Thanks ahead of time for looking into this.
I finally got to find the answer.
I've had several reactive element duplicated across different tabs. For some reason Shiny does not like this. Once I've created different reactive strings (in my case) then everything was fine (tabitems with renderUI in server.r, as well as sidebarmenu).
Weird but anyway.
Related
I am creating a dynamic shiny app that works like a look up table -- it allows users to select input values and in return gives two corresponding output values (one numeric and one character) which exist in the same table.
My code needs to be dynamic, so that when the data frame changes, the user interface changes accordingly. For example, if the data table contains 3 input variables instead of 2, there needs to be one more selectInput box in the sidebar. If one variable ends up having 3 possible values instead of 2, there needs to be another option.
Thus, my code needs to:
check the updated table,
see how many variables there are and update input options in the sidebar accordingly
update range of values each of these variables has
Update the output accordingly.
Below is a simplified code:
{
library(shiny)
library(shinydashboard)
library(shinyjs)
}
Test <- data.frame(
stringsAsFactors = FALSE,
input1 = c("precarious", "precarious", "good"),
input2 = c("precarious", "moderate", "precarious"),
NumericOutput = c(3.737670877,6.688008306,8.565495761),
CharacterOutput = c("precarious", "moderate", "good")
)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
useShinyjs(),
selectInput("a", label = colnames(Test[1]),
choices = unique(Test[[1]])),
selectInput("b", colnames(Test[2]),
choices = unique(Test[[2]]))
),
dashboardBody(fluidRow(valueBoxOutput("info_box1", width = 6)),
fluidRow(valueBoxOutput("info_box2", width = 6)))
)
server <- function(input, output) {
output$info_box1 <- renderValueBox({
valueBox(
value = paste0("Score in %: ",
Test$NumericOutput[Test$input1 == input$a & Test$input2 == input$b],
collapse = ", "),
subtitle = NULL)
})
output$info_box2 <- renderValueBox({
valueBox(value = paste0(
"Assessment: ",
Test$CharacterOutput[Test$input1 == input$a & Test$input2 == input$b],
collapse = ", "),
subtitle = NULL)
})
}
shinyApp(ui, server)
Here is the outline of code. I've adopted logic you provided - input cols are the ones on which filtering is done, ouput cols are the ones on which some aggregation is done. You requested only dynamic filtering and not the output. data is reactive because from your text it's obvious you want to change datasets. Code inside its reactivity is something you need to come up with because you didn't provide any information beside Test data.frame.
library(shiny)
library(shinydashboard)
library(shinyjs)
Test <- data.frame(
stringsAsFactors = FALSE,
input1 = c("precarious", "precarious", "good"),
input2 = c("precarious", "moderate", "precarious"),
NumericOutput = c(3.737670877,6.688008306,8.565495761),
CharacterOutput = c("precarious", "moderate", "good")
)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
uiOutput("dynamicSidebar")
),
dashboardBody(fluidRow(valueBoxOutput("info_box1", width = 6)),
fluidRow(valueBoxOutput("info_box2", width = 6)))
)
server <- function(input, output){
rv <- reactiveValues()
data <- reactive({Test})
output$dynamicSidebar <- renderUI({
req(data())
rv$input_cols <- names(data()) %>% str_subset("^input")
input_values <- data() %>%
select(rv$input_cols) %>%
map(unique)
rv$input_cols %>%
map2(input_values, ~selectInput(.x, .x, choices = .y))
})
observe({
cond <- reactiveValuesToList(input) %>%
.[rv$input_cols] %>%
imap(~str_c(.y, "=='", .x, "'")) %>%
str_c(collapse = "&")
rv$filtered_data <- data() %>%
filter(eval(parse(text = cond)))
})
output$info_box1 <- renderValueBox({
req(rv$filtered_data)
my_value <- if(nrow(rv$filtered_data) > 0){
str_c(rv$filtered_data[["NumericOutput"]],collapse = ", ")
} else {
"empty data"
}
valueBox(
subtitle = "Score in %: ",
value = my_value
)
})
output$info_box2 <- renderValueBox({
req(rv$filtered_data)
my_value <- if(nrow(rv$filtered_data) > 0){
str_c(rv$filtered_data[["CharacterOutput"]], collapse = ", ")
} else {
"empy data"
}
valueBox(
subtitle = "Assessment:",
value = my_value
)
})
}
shinyApp(ui, server)
I'm trying to create an UI in which user can choose some objects (as many as they want) and their respective weights. The weight input fields appear only when there's more than one object and increase as the user selects more objects. This part already works.
What I need is a vector that holds all the weights saved in the w1, w2 and so on.
I've tried using for loops and sapply with get() function but can't access the input$w1, input$w2 etc.
library(shiny)
# Create list of objects
object_list <- vector()
object_list <- paste0("O_", 1:10)
names(object_list) <- paste("Object", 1:10)
ui <- dashboardPage(
dashboardHeader(title = "Dynamic UI"),
dashboardSidebar(
width = 700,
fluidRow(
column(7, selectInput("chosen_objects", "Chosen objects", choices = object_list, multiple = TRUE, width = "100%")),
column(5, uiOutput("weights"))
)
),
dashboardBody(
fluidPage(tabBox(width=2500,
tabPanel(
title = "Table"
)
)
)
)
)
server <- function(input,output) {
objects_number <- reactive({length(input$chosen_objects)})
output$weights <- renderUI({
if (is.na(objects_number()) | objects_number() <= 1)
return(NULL)
lapply(1:objects_number(), function(i) {
id <- paste0("w", i)
textInput(id, paste("Weight of", input$chosen_objects[i]), value = input[[id]], width = "50%", placeholder = "%")
})
})
}
shinyApp(ui, server)
Is there a way to collect the dynamic inputs in one vector or list?
I have made a few changes and based on your code I think you are good enough to see and get them by yourself. Let me know if you have any questions -
library(shiny)
# Create list of objects
object_list <- vector()
object_list <- paste0("O_", 1:10)
names(object_list) <- paste("Object", 1:10)
ui <- dashboardPage(
dashboardHeader(title = "Dynamic UI"),
dashboardSidebar(
width = 700,
fluidRow(
column(7, selectInput("chosen_objects", "Chosen objects",
choices = object_list, multiple = TRUE, width = "100%")),
column(5, uiOutput("weights"))
)
),
dashboardBody(
fluidPage(tabBox(width=2500,
tabPanel(
title = "Table",
verbatimTextOutput("weight_output")
)
)
)
)
)
server <- function(input,output) {
objects_number <- reactive({length(input$chosen_objects)})
output$weights <- renderUI({
if (is.na(objects_number()) | objects_number() <= 1)
return(NULL)
lapply(gsub("[A-Z]+_", "", input$chosen_objects), function(i) {
id <- paste0("w", i)
textInput(id, paste("Weight of", paste0("O_", i)),
value = NULL, width = "50%", placeholder = "%")
})
})
output$weight_output <- renderPrint({
req(input$chosen_objects)
sapply(paste0("w", gsub("[A-Z]+_", "", input$chosen_objects)), function(a) input[[a]])
})
}
shinyApp(ui, server)
How do I create a scrollable list of tables within a tabPanel?
Based on Outputing N tables in shiny, where N depends on the data, I have tried the following
Server.R
userHist <- list(
data.frame(X=1:10,Y=11:20),
data.frame(X=1:10,Y=11:20))
output$groupHistory <- renderUI({
userHistList <- lapply( seq(userHist), function(i){
hist_i <- userHist[[i]]
TabName <- paste0("User", i)
fluidRow( column(10,
h2(TabName),
hr(),
column(3, renderTable(hist_i, rownames=TRUE) )
) )
} )
userHistList
})
ui.R
tabsetPanel(id="tabsetpanel",
tabPanel(h1("Group History"),
style="overflow-y:scroll;",
uiOutput("groupHistory")
)
)
There is a main firefox scrollbar that shows up when the list gets long, but there is a second scrollbar for the table that does not scroll vertically. Ideally I would also eliminate horizontal scrolling.
You need to call the render first to create the output objects and the compose the UI with those objects:
ui <- fluidPage(
tabsetPanel(
id = "tabsetpanel",
tabPanel(
style = "overflow-y:scroll; max-height: 600px",
h1("Group History"),
numericInput("n_users", "Number of Users", value = 5, min = 1, max = 10),
uiOutput("group_history")
)
)
)
server <- shinyServer(function(input, output) {
df_list <- reactive({
n <- input$n_users
# generate some observations
obs_x <- seq(3)
obs_y <- obs_x + n
# generate the df
df_template <- data.frame(x = obs_x, y = obs_y)
# make a list of df and return
lapply(seq(n), function(n) {
df_template
})
})
# use the constructed renders and compose the ui
output$group_history <- renderUI({
table_output_list <- lapply(seq(input$n_users), function(i) {
table_name <- paste0("table", i)
tab_name <- paste("User", i)
fluidRow(
column(
width = 10,
h2(tab_name),
hr(), column(3, tableOutput(table_name))
)
)
})
# Convert the list to a tagList - this is necessary for the list of items
# to display properly.
do.call(tagList, table_output_list)
})
# Call renderTable for each one. Tables are only actually generated when they
# are visible on the web page.
observe({
data <- df_list()
for (i in seq(input$n_users)) {
# Need local so that each item gets its own number. Without it, the value
# of i in the renderPlot() will be the same across all instances, because
# of when the expression is evaluated.
local({
my_i <- i
tab_name <- paste0("table", my_i)
output[[tab_name]] <- renderTable(data[[my_i]], rownames = TRUE)
})
}
})
})
shinyApp(ui, server)
Based off of Winston Chang's work here
I wrapped the list in fluidPage or wellPanel and everything works as I want.
Server.R
userHist <- list(
data.frame(X=1:10,Y=11:20),
data.frame(X=1:10,Y=11:20))
output$groupHistory <- renderUI({
userHistList <- lapply( seq(userHist), function(i){
hist_i <- userHist[[i]]
TabName <- paste0("User", i)
fluidRow( column(10,
h2(TabName),
hr(),
column(3, renderTable(hist_i, rownames=TRUE) )
) )
} )
table_output_list <- fluidPage(userHistList,
style="overflow-y:scroll; max-height: 90vh")
})
UI.R
tabsetPanel(id="tabsetpanel",
tabPanel(h1("Group History"),
style="overflow: visible",
uiOutput("groupHistory")
)
)
I'm building a simple shiny app which will take inputs from the user and fetch data from a table in the DB and take the number of records to be downloaded as an input and provide a download file option.
Everything below works just fine. My only concern is the textInput bar( variable : uiOutput("text") in the ui and output$text in the server) appears only after the datatableOutput is displayed. I do not understand why this happens.
Ideally, I want the textInput bar ('uiOutput("text")') object to be displayed once the leaf(i.e. input$leaf1 is not null) is selected and then I want the datatableOutput to be displayed and then the Download Button should come up.
Is there a way I can achieve this? Thanks
library(shiny)
library(shinydashboard)
#library(stringr)
library(DT)
#library(shinyBS)
ui <- dashboardPage(
dashboardHeader(title = strong("DASHBOARD"),titleWidth = 240),
dashboardSidebar(
sidebarMenu(
selectizeInput("x", "Choose a number:", choices = sort(unique(lftable$x)), multiple = TRUE),
uiOutput("leaf_categ")
)
),
dashboardBody(
fluidRow(
uiOutput("text"),
dataTableOutput("lm_df"),
downloadButton('downloadData', 'Download')
)))
server <- function(input, output){
output$leaf_categ <- renderUI(
selectizeInput("leaf1", "Choose leaf categories:",
choices = reactive(unique(lftable[lftable$num %in% input$x, c("X_NAME")]))(),
multiple = TRUE)
)
#### creates a text input box
#### number of records to be downloaded is provided as input
output$text <- renderUI({
if(is.null(reactive(input$leaf1)())){
return()
}else{
textInput("var1", label = "Enter the number of records to be downloaded", value = "")
}
})
#### fetches data from DB
lm <- reactive({
if(is.null(input$leaf1)){
return()
}else{
leaf_id <- unique(lftable[lftable$X_NAME %in% input$leaf1, c("leaf_id")])
query_str <- paste('select * from table1 where current_date between start_dt and end_dt and score_num >= 0.1 and x in (' , input$x, ')', ' and X_ID in (', leaf_id, ')', ';', sep = "")
}
lm_data <- getDataFrmDW(query_str)
})
###creates a download tab
output$downloadData <- downloadHandler(
filename = function() { paste("lm_user_data", '.csv', sep='') },
content = function(file) {
lm_df <- lm()
lm_df <- lm_df[1:(as.integer(input$text)),]
print(dim(lm_df))
write.csv(lm_df, file, row.names = F)
})
output$lm_df <- DT::renderDataTable(lm())
}
shinyApp(ui, server)
Fowllowing the description of dynamic shiny app at topic [R Shiny Dynamic Input
, i want to get a data into shiny app. I wrote in ui.R
library(fPortfolio)
library(quantmod)
library(shiny)
ui <- shinyUI(fluidPage(
titlePanel("Portfolio optimization"),
sidebarLayout(
sidebarPanel(
numericInput("numInputs", "A number of stocks", 2),
# place to hold dynamic inputs
uiOutput("inputGroup")
),
mainPanel(
tabPanel("Trading Statistics",
fixedRow(
column(8,
fixedRow(column(4,tableOutput("tablePerformance")),
column(4,tableOutput("tableRisk"))),
fixedRow(column(4,tableOutput("tableDaily")),
column(4,tableOutput("tableMonthly"))))
))
)
)
))
and in server.r
library(fPortfolio)
library(quantmod)
library(shiny)
server<-shinyServer(function(input, output){
observeEvent(input$numInputs, {
output$inputGroup = renderUI({
input_list <- lapply(1:input$numInputs, function(i) {
# for each dynamically generated input, give a different name
inputName <- paste("input", i, sep = "")
textInput(inputName, inputName, value = 1)
})
do.call(tagList, input_list)
})
})
data <- read.csv("E:/stock vn data/dulieuvietnam/metastock_all_data.txt", header = TRUE, stringsAsFactors = FALSE)
Tickers <- data[!duplicated(data$X.Ticker.),1]
Tickers <- subset(Tickers,substr(Tickers,1,1)!= "^")
PriceList <- list()
for (i in 1:length(Tickers)){
PriceList[[i]] <- subset(data[,c(2,6)],data$X.Ticker. == Tickers[i])
names(PriceList[[i]]) <- c("Date",Tickers[i])
PriceList[[i]][PriceList[[i]]==0]<-NA
PriceList[[i]] <- na.locf(PriceList[[i]])
}
PriceList[[(length(Tickers)+1)]]<-subset(data[,c(2,6)],data$X.Ticker. == "^VNINDEX")
names(PriceList[[(length(Tickers)+1)]]) <- c("Date","VNINDEX")
PriceList[[(length(Tickers)+1)]][PriceList[[(length(Tickers)+1)]]==0]<-NA
PriceList[[(length(Tickers)+1)]] <- na.locf(PriceList[[(length(Tickers)+1)]])
dataPrice <- PriceList[[1]]
for (k in 2:length(PriceList)){
dataPrice <-merge(dataPrice,PriceList[[k]],all=TRUE)
}
output$tablePerformance<-renderTable({
})
})
.
When i run runApp(), the app only shows input with label "A number of stocks" that has default value is 2. However, interface of app did not show two text input.
Please help me!