I have written a Shiny code that has a dashboard with the following elements
1) Side Bar Layout
2) On clicking Tab 'view', the main panel gets populated with tabset panel
3) On clicking 'table', two selectInput should be displayed where the sheet dropdown is dependent on Table dropdown. The Datasets are read from xlsx files
library(shinydashboard)
library(leaflet)
library(ggplot2)
library(DT)
library(openxlsx)
# -----------------------------------------------------------------------------
# Dashboard UI
# -----------------------------------------------------------------------------
dataset <- c("P1-Long-Term-Unemployment-Statistics","P1-OfficeSupplies")
ui <- dashboardPage(
dashboardHeader(
title = "Validation Tool"
),
dashboardSidebar(
sidebarMenu(
menuItem("View Tables", tabName = "view", icon = icon("database")),
menuItem("Append Data", tabName = "append", icon = icon("database")),
menuItem("Update Table", tabName = "update", icon = icon("crosshairs")),
menuItem("Construct Table", tabName = "construct", icon = icon("fire"))
),
div(style = "padding-left: 15px;padding-right: 5px; padding-top: 40px;",
p(class = "small", "Note : This validation tools automates the mainstream process involved in creating a Master data for detailed analysis ")
)
),
dashboardBody(
tabItems(
# Current location ------------------------------------------------------
tabItem(tabName = "view",
mainPanel(
titlePanel(h2("Explore Datasets")),fluidRow(
column(8,
selectInput("table",
"Table:",
dataset)
),
column(8,
uiOutput("sheets")
),
DT::dataTableOutput("table")
),
tabsetPanel(type="tab",
tabPanel("Data"
),
tabPanel("Summary"),
tabPanel("Plot")
)
)
)
)
)
)
# -----------------------------------------------------------------------------
# Dashboard server code
# -----------------------------------------------------------------------------
server <- function(input, output) {
file <- reactive({paste0("D:/Dataset/",input$table,".xlsx")})
sheetNames <- reactive({getSheetNames(file)})
output$sheets <- renderUI({
selectInput("sheet","Sheet:",choices = sheetNames)
})
output$table <- DT::renderDataTable(DT::datatable({
data <- read.xlsx(file,sheet=as.numeric(input$sheet))
data
}))
}
shinyApp(ui, server)
But while implementing the above, I get the error (screenshot attached)
"Error : Invalid 'description' argument"
"Error : cannot coerce type 'closure' to vector of type 'list'
Please help resolving the issue
You have to call reactive values with parentheses (file reactive value declared in line 62). But there is a file() function in base R, so change this e.g. for my_file and call it with parentheses, e.g.:
my_file <- reactive({paste0("D:/Dataset/",input$table,".xlsx")})
sheetNames <- reactive({getSheetNames(my_file())})
The below code works fine
server <- function(input, output) {
my_file <- function(){
my_file <- paste0("D:/Dataset/",input$table,".xlsx")
}
sheetNames <- function(){
sheetNames <- getSheetNames(my_file())
}
output$sheets <- renderUI({
selectInput("sheet","Sheet:",choices = sheetNames())
})
output$table <- DT::renderDataTable(DT::datatable({
data <- read.xlsx(my_file(),sheet=as.character(input$sheet))
data
}))
}
Related
I am trying to read data and if the data has observation , the further UI has to expand. But the conditional Pannel is not working .
Here is the part of the UI and server code.
UI Code:
tabPanel("DMC 1.1", fluid = TRUE, useShinyjs(),
column(width=12,wellPanel(div(style = 'height:120px;',
fluidRow(column(width=9,style = "font-size: 12px;",fileInput("analysis_data2", label = "Import data", accept = c(".csv",".sas7bdat",".xls",".xpt"))))
)),
conditionalPanel(condition = "output.dataUpload", wellPanel(fluidRow(
column(width=5,textInput("subset","Subsettign Condition",value="")),
column(width=5,textInput("byvar","By Variable",value="")),
column(width=5,textInput("subgrp","SubGroup Variable",value="")),
column(width=5,textInput("trtvar","Treatment Variable",value="")),
column(width=5,textInput("xaxisvar","X Axis Variable",value="")),
column(width=5,textInput("yaxisvar","Y Axis Variable",value=""))
)))
)
)
Server Code:
#################Tab 2 - DMC 2.0#########################
analysis_d <- reactive({data_read(datain=input$analysis_data2)})
output$dataUpload <- reactive({return(!is.null(isolate(analysis_d())))})
outputOptions(output, 'dataUpload', suspendWhenHidden=FALSE)
This app will make an additional text input files available in the UI if the uploaded csv file contains at least one row:
library(shiny)
ui <- fluidPage(
fileInput("file", "File"),
conditionalPanel(
condition = "output.available",
textInput("additional_input", "additional input")
)
)
server <- function(input, output, session) {
data <- reactive(read.csv(input$file$datapath))
output$available <- reactive(nrow(data()) >= 1)
outputOptions(output, "available", suspendWhenHidden = FALSE)
}
shinyApp(ui, server)
I need to develope a shiny interface with many csv loaded in it. Based on my past experience with shiny, I prefer to import this data before the server function, in this way I hope that each session will run faster. The app will be restarted each morning, so data will be daily refreshed.
The problem is that I need to consider an extra refresh during the day, performed manualy with a button that source an external updating script.
I can't (but I hope that is possibele), refresh the data loaded at the start of the app. Below my (dummy) code:
server:
library(shinydashboard)
library(plotly)
library(data.table)
library(dplyr)
path1<-"C:/Users/.../DATA/"
path2<-"C:/Users/../DATA/csv/"
##load dataset at first start
table<-fread(file=paste0(path2,"main.csv"),data.table=FALSE))
shinyServer(function(input, output,session) {
##### refresh data with button####
observeEvent(input$refresh_data,{
source(paste0(path1,"any_script.r"),local = FALSE)
table<<-fread(file=paste0(path2,"main.csv"))
})
#####...ui####
table_r<-reactive({
##obs populate the input for choosing rows to be plotted
obs<-rev(unique(table$anycolumn))
curve_sint<-list(
lotti=obs,
data=obs
)
})
output$obs_ui<-renderUI({
selectInput("input_obs","Please choose the batch:",
choices =table()$obs ,multiple = T)
})
output$plot<-renderPlotly({
table_r()$data%>%
filter(anycolumn==input$input_obs)%>%
plot_ly(
x=~x,
y=~y,
color=~anycolumn,
type="scatter"
)
})
})
ui:
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(
title = "shiny"
),
dashboardSidebar(
width=250,
sidebarMenu(
menuItem(
"plot data"
tabName = "clhc",
icon = NULL
),
menuItem(
"Update data",
icon=icon("gear"),
tabName="update_data"
)
)
),
dashboardBody(
tabItems(
tabItem(
tabName = "clhc",
fluidRow(
column(width=3,
uiOutput("obs_ui")
),
),
fluidRow(
column(
width=12,
fluidRow(
plotlyOutput("plot")
)
)
)
),
tabItem(
tabName = "update_data",
fluidRow(
box(
width=12,
title="Sint HC",
actionButton("refresh_sint_hc","Refresh", icon=icon("refresh"))
)
)
)
)
)
)
I'm sure that the script inside observeevent works fine, because if I put a print(nrow(table)) after the fread I can see that table is correctly refreshed. I can't understand where the new data is stored because from the plot panel is stil available the old data before the update.
Is my attempt completley wrong?
Using <<- will make table accessible globally, and after terminating your shiny app, but you need it to be reactive. Here is a brief example on using a reactiveVal (setting to table1 as default) that gets modified when the actionButton is selected and a new data file is read.
library(shiny)
library(data.table)
table1 <- fread(file = 'atest1.csv')
ui <- fluidPage(
verbatimTextOutput("text"),
actionButton("refresh", "Refresh")
)
server <- function(input, output, session) {
rv <- reactiveVal(table1)
output$text <- renderText({
names(rv())
})
observeEvent(input$refresh, {
print("Refresh")
table1 <<- fread(file = 'atest2.csv')
rv(table1)
})
}
shinyApp(ui, server)
I came across this issue while trying to user shiny::renderUI to generate a data table output via renderDataTable upon clicking an actionButton. This situation works fine until I try to implement two instances of the same thing in separate tabs. In this case, whichever button is clicked first (be it in tab 1 or tab 2) works correctly; but then the other tab's button doesn't produce the data table. Is there a way to get two buttons, in separate shinydashboard tabs, to render data tables independently?
The following shows reproducible code to demonstrate the issue. A small data frame is populated with random values. Clicking the action button calculates new numbers for the data table--but only for the first data table that is rendered.
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(title = "Test example"),
dashboardSidebar(
sidebarMenu(
menuItem("Tab 1", tabName = "tab_1"),
menuItem("Tab 2", tabName = "tab_2")
)
),
dashboardBody(
tabItems(
tabItem("tab_1",
h2("Tab 1"),
fluidRow(
actionButton("do_refresh_tab_1", "Refresh data")
),
fluidRow(
uiOutput("tab1")
)
),
tabItem("tab_2",
h2("Tab 2"),
fluidRow(
actionButton("do_refresh_tab_2", "Refresh data")
),
fluidRow(
uiOutput("tab2")
)
)
)
)
)
server <- function(input, output, session) {
observeEvent(input$do_refresh_tab_1, {
df <- data.frame(value = rnorm(3),
Measurement = rnorm(3),
stringsAsFactors = FALSE)
output$tab1 <- renderUI({
output$temp <- renderDataTable(df)
dataTableOutput("temp")
})
})
observeEvent(input$do_refresh_tab_2, {
df <- data.frame(value = rnorm(3),
Measurement = rnorm(3),
stringsAsFactors = FALSE)
output$tab2 <- renderUI({
output$temp <- renderDataTable(df)
dataTableOutput("temp")
})
})
}
shinyApp(ui, server)
Before we go to the solution, a couple of general rules of thumb.
Avoid, in fact, never put a render call inside another render call.
Never put a render call inside an observe call
Never put a render call inside a reactive call
Each observe, reactive and render call should be standalone and must perform 1 task/function.
The reason why only the first click was working and the second click on the other tab was not, was because you were attempting to create multiple output bindings with the same id (temp).
Every output element must have its own unique id.
Also, using uiOutput and dataTableOutput for this use case is kinda redundant here.
Here is the simplified code,
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(title = "Test example"),
dashboardSidebar(
sidebarMenu(
menuItem("Tab 1", tabName = "tab_1"),
menuItem("Tab 2", tabName = "tab_2")
)
),
dashboardBody(
tabItems(
tabItem("tab_1",
h2("Tab 1"),
fluidRow(
actionButton("do_refresh_tab_1", "Refresh data")
),
fluidRow(
dataTableOutput("table1")
)
),
tabItem("tab_2",
h2("Tab 2"),
fluidRow(
actionButton("do_refresh_tab_2", "Refresh data")
),
fluidRow(
dataTableOutput("table2")
)
)
)
)
)
server <- function(input, output, session) {
output$table1 <- renderDataTable({
req(input$do_refresh_tab_1)
df <- data.frame(value = rnorm(3),
Measurement = rnorm(3),
stringsAsFactors = FALSE)
return(df)
})
output$table2 <- renderDataTable({
req(input$do_refresh_tab_2)
df <- data.frame(value = rnorm(3),
Measurement = rnorm(3),
stringsAsFactors = FALSE)
return(df)
})
}
shinyApp(ui, server)
I'm building a Shiny app and for the last two days I'm being blocked on the following step: I've put a "Submit" button on a typeform and apparently there are no problems, but everytime I run the app I can't click on it because for the very beginning it shows me a "no trespassing" signal disallowing me to do nothing else.
Here's the code I'm using:
# Packages ----
if(require("pacman")=="FALSE"){
install.packages("pacman")
}
library(pacman)
pacman::p_load(dplyr, tidyr, shiny, shinydashboard)
# Global scope ----
dish <- c("Salad", "Spaghetti Carbonara", "Scrambled eggs")
allergens <- c("sesame", "lactose", "eggs")
keywords <- c("veggie", "pasta", "none")
dishes <- data.frame(dish, allergens, keywords)
# Function to label mandatory fields ----
labelMandatory <- function(label) {
tagList(
label,
span("*", class = "mandatory_star")
)
}
appCSS <- ".mandatory_star { color: red; }" #to make the asterisk red
MandatoryFields_dishes <- c(names(dishes[,-3]))
fields_dishes <- c(names(dishes))
ui <- dashboardPage(
dashboardHeader(title = "sample"),
dashboardSidebar(
sidebarMenu(
menuItem("Dishes", tabName = "dishes")
)
),
dashboardBody(
# Dishes
tabItems(
tabItem(tabName = "dishes",
tabsetPanel(
tabPanel("Typeform",
fluidPage(
shinyjs::useShinyjs(),
shinyjs::inlineCSS(appCSS),
titlePanel("Dish introduction"),
div(
id="form",
textInput("dish", labelMandatory("Dishes"), ""),
textInput("allergens", label = "Allergens",""),
textInput("keyword", label = "Keyword", ""),
actionButton("submit", "Submit", class="btn-primary")
)
))
))
))
)
server <- function(input, output) {
observe({
mandatoryFilled_dishes <-
vapply(MandatoryFields_dishes,
function(x) {
!is.null(input[[x]]) && input[[x]] != ""
},
logical(1))
mandatoryFilled_dishes <- all(mandatoryFilled_dishes)
shinyjs::toggleState(id = "submit", condition = mandatoryFilled_dishes)
})
}
shinyApp(ui, server)
I guess I'm missing something on the server. If someone could help me I'll be very grateful, lots of thanks in advance.
I need my Shiny module to hide/show a div outside of the namespace. I tried passing the div id to the module server function and using shinyjs to show/hide it but that is not working. I'm not getting an error, it just doesn't show/hide the div.
I know the Shiny module documentation says modules cannot access outputs outside the namespace. The docs do, though, give a way for the module to access inputs outside the namespace using reactives.
Does anyone know if there is a way for a Shiny module to access an output outside the namespace?
Here is what I'm trying to do:
### ui.R ###
header <- dashboardHeader(
title = a(href = 'http://google.com')
)
dashboardPage(
skin = 'black',
header,
dashboardSidebar(
sidebarMenu( id='tabs',
menuItem('Edit Existing Client', tabName = 'client-info')
)),
dashboardBody(
useShinyjs(),
fluidRow(
tabItems(
tabItem(tabName = "client-info",
div(selectClientModuleUI("clientinfons")),
div(id='editclientinfo', uiOutput('editclientstuff'))
)
)
)
)
)
### server.R ###
shinyServer(function(session,input, output) {
output$editclientstuff <- renderUI({
div(
fluidRow(
column(6,
textInput('editname', "Display name", value ='Testing name')
),
column(6,
numericInput('editastart','Start', value ='3')
)
)
)
})
callModule(selectClientModule, 'clientinfons', 'editclientinfo')
shinyjs::hide(id='editclientstuff')
})
### in global.R ###
selectClientModuleUI <- function(id){
ns <- NS(id)
clientlist = c(0, 1, 2)
names(clientlist) = c('Choose client', 'Fred', 'Kim')
div(
selectInput(ns('selectclient'), 'Select client to edit', choices = clientlist, selected = NULL, multiple = FALSE)
)
}
selectClientModule <- function(input, output, session, divtoshow = ''){
observeEvent(input$selectclient, {
if (!is.null(input$selectclient) && input$selectclient > 0){
print(paste0("showing ", divtoshow))
shinyjs::show(divtoshow)
}
})
}
That is possible by giving the value as a reactive (not as the value of the reactive) to the module. You can change the reactive Value in the module and return the reactive from the Module to the app (note, return the reactive itself, not its value). The following app switches the 'divtoshow' in the main app from inside the module. If nothing is selected, it's hidden, otherwise it's shown (note, I adjusted you code a little so it's working as a stand-alone app):
library(shinydashboard)
library(shinyjs)
# Module
selectClientModuleUI <- function(id){
ns <- NS(id)
clientlist = c(0, 1, 2)
names(clientlist) = c('Choose client', 'Fred', 'Kim')
div(
selectInput(ns('selectclient'), 'Select client to edit', choices = clientlist, selected = NULL, multiple = FALSE)
)
}
selectClientModule <- function(input, output, session, divtoshow){
observeEvent(input$selectclient, {
if (input$selectclient > 0){
print(paste0("showing editclientinfo"))
divtoshow("editclientinfo") # set the div to show to "editclientinfo", this will be visible outside the module
}else{
divtoshow("") # set the div to show to "", if nothing was chosen
}
})
# return the div to show as reactive to the main app
return(divtoshow)
}
# Main App
ui <- shinyUI(
dashboardPage(
skin = 'black',
dashboardHeader(
title = a(href = 'http://google.com')
),
dashboardSidebar(
sidebarMenu( id='tabs',
menuItem('Edit Existing Client', tabName = 'client-info')
)),
dashboardBody(
useShinyjs(),
fluidRow(
tabItems(
tabItem(tabName = "client-info",
div(selectClientModuleUI("clientinfons")),
div(id='editclientinfo', uiOutput('editclientstuff'))
)
)
)
)
))
server <- shinyServer(function(session,input, output) {
output$editclientstuff <- renderUI({
div(
fluidRow(
column(6,
textInput('editname', "Display name", value ='Testing name')
),
column(6,
numericInput('editastart','Start', value ='3')
)
)
)
})
# store the div to show in a reactive
divtoshow <- reactiveVal('')
# divtoshow can be changed in side this module, so it's a return value
divtoshow <- callModule(selectClientModule, 'clientinfons', divtoshow)
# observe the value of divtoshow and toggle the corresponding div
observeEvent(divtoshow(), {
if(divtoshow() == "editclientinfo"){
shinyjs::show("editclientinfo")
}else{
shinyjs::hide("editclientinfo")
}
})
})
shinyApp(ui, server)