I am trying to separate the functionality of my Shiny application in order to make it reusable.
I have my ui. R file where I define :
tabPanel("Unemployed", source("unemployed_select.R", local=TRUE)$value),
and in my unemployed_select.R I define:
fluidPage(
titlePanel("Basic DataTable"),
# Create a new Row in the UI for selectInputs
fluidRow(
column(4,
selectInput("man",
"Manufacturer:",
c("All",
unique(as.character(mpg$manufacturer))))
),
column(4,
selectInput("trans",
"Transmission:",
c("All",
unique(as.character(mpg$trans))))
),
column(4,
selectInput("cyl",
"Cylinders:",
c("All",
unique(as.character(mpg$cyl))))
)
),
# Create a new row for the table.
fluidRow(
DT::dataTableOutput("table")
)
)
My server.R file is :
library(shiny)
library(shinythemes)
library(dataset)
shinyServer(function(input, output) {
# Filter data based on selections
output$table <- DT::renderDataTable(DT::datatable({
data <- mpg
if (input$man != "All") {
data <- data[data$manufacturer == input$man,]
}
if (input$cyl != "All") {
data <- data[data$cyl == input$cyl,]
}
if (input$trans != "All") {
data <- data[data$trans == input$trans,]
}
data
}))
})
I used the code from a well-known example in R gallery https://shiny.rstudio.com/gallery/basic-datatable.html
just to be sure that have no problems of data. Still datatable is not rendering so I guess it has to be a problem with defining inside source file unemployed_select.R.
Any ideas?
Regards
You are right that you need to use source() to load your module file, but with Shiny, you need to be aware of namespaces. The module and the file it is sourced in must share a namespace, wherein names for things are shared. For example, in your module code, you have this line:
column(4,
selectInput("man",
"Manufacturer:",
c("All",
unique(as.character(mpg$manufacturer))))
But you want the module to share the namespace of the file it is included in, so you need to have a way to let the file, which is including the module, know which parts are ids, like "man" and which parts are serious arguments like "Manufacturer:"
So in a Shiny Module, that line would become
column(4,
selectInput(ns("man"),
"Manufacturer:",
c("All",
unique(as.character(mpg$manufacturer))))
Here the ns() function is used to include the id in the namespace, this will allow your declared id "man" to be usable by the rest of the app.
There is a great guide to namespaces and writing modules in Shiny here:
https://shiny.rstudio.com/articles/modules.html
The link above points out that you must namespace ids, must make your module fit into a function and call that function using callModule() from your ui.R file, and must wrap everything in a tagList instead of a fluidPage.
Best of luck!
Related
I'm building a shiny app and I need one of the tabpanels to show the R Documentation of one of the functions of the package RecordLinkage, so the user can choose his argument options.
I have tried
library(shiny)
library(RecordLinkage)
ui <- fluidPage(
tabPanel("Literature of functions",
selectInput(
"literatureChoice",
"Choose a function : ",
choices = c("compare.dedup",
"compare.linkage")
),
textOutput("literatureOutput")
),
)
server <- function(input, output) {
output$literatureOutput <- renderText({
?compare.linkage
})
}
But it doesn't show any documentation. I'm aware that ?compare.linkage shows in help panel in RStudio instead of the console.
R help documentations are stored in Rd objects. We need a way to fetch this object and render them in Shiny.
We fetch the Rd object with the help of Rd_fun() found in the gbRd package.
We then parse the file into html with Rd2HTML. The file is saved in the temp directory and read back into R.
The example is for the reactive() function found in the shiny package, replace it with whichever function required.
library(shiny)
library(gbRd)
library(tools)
ui <- fluidPage(
tabPanel("Literature of functions",
selectInput(
"literatureChoice",
"Choose a function : ",
choices = c("compare.dedup",
"compare.linkage")
),
htmlOutput("literatureOutput")
)
)
server <- function(input, output) {
output$literatureOutput <- renderText({
temp = Rd2HTML(Rd_fun("reactive"),out = tempfile("docs"))
content = read_file(temp)
file.remove(temp)
content
})
}
shinyApp(ui,server)
Description
I have a textAreaInput box that I want to start with a default value. The user can click 2 actionButtons (Submit & Random Comment). Submit updates the comment from the textAreaInput for further processing (plot, etc.) while Random Comment sends a new random value to textAreaInput (the user may type in the textAreaInput box as well). I almost have it but can't get the app to update textAreaInput's value until the Submit button is pressed.
Question
I want it to be updated when Random Comment is pressed but still allow the user to erase the text box and type their own text. How can I make the app do this?
MWE
library(shiny)
library(shinyjs)
library(stringi)
shinyApp(
ui = fluidPage(
column(2,
uiOutput("randcomment"),
br(),
div(
actionButton("randtext", "Random Comment", icon = icon("quote-right")),
div(actionButton("submit", "Submit", icon = icon("refresh")), style="float:right")
)
),
column(4, div(verbatimTextOutput("commenttext"), style = 'margin-top: 2cm;'))
),
server = function(input, output) {
output$randcomment <- renderUI({
commentUi()
})
comment_value <- reactiveValues(default = 0)
observeEvent(input$submit,{
comment_value$default <- input$randtext
})
renderText(input$randtext)
commentUi <- reactive({
if (comment_value$default == 0) {
com <- stri_rand_lipsum(1)
} else {
com <- stri_rand_lipsum(1)
}
textAreaInput("comment", label = h3("Enter Course Comment"),
value = com, height = '300px', width = '300px')
})
output$commenttext <- renderText({ input$comment })
}
)
I'd approach this a little bit differently. I would use reactiveValues to populate both of the fields, and then use two observeEvents to control the contents of the reactiveValues.
I don't think you need a reactive at all in this situation. reactive is good when you want immediate processing. If you want to maintain control over when the value is processed, use reactiveValues.
library(shiny)
library(shinyjs)
library(stringi)
shinyApp(
ui = fluidPage(
column(2,
uiOutput("randcomment"),
br(),
div(
actionButton("randtext", "Random Comment", icon = icon("quote-right")),
div(actionButton("submit", "Submit", icon = icon("refresh")), style="float:right")
)
),
column(4, div(verbatimTextOutput("commenttext"), style = 'margin-top: 2cm;'))
),
server = function(input, output) {
# Reactive lists -------------------------------------------------------
# setting the initial value of each to the same value.
initial_string <- stri_rand_lipsum(1)
comment_value <- reactiveValues(comment = initial_string,
submit = initial_string)
# Event observers ----------------------------------------------------
observeEvent(input$randtext,
{
comment_value$comment <- stri_rand_lipsum(1)
}
)
# This prevents the comment_value$submit from changing until the
# Submit button is clicked. It changes to the value of the input
# box, which is updated to a random value when the Random Comment
# button is clicked.
observeEvent(input$submit,
{
comment_value$submit <- input$comment
}
)
# Output Components -------------------------------------------------
# Generate the textAreaInput
output$randcomment <- renderUI({
textAreaInput("comment",
label = h3("Enter Course Comment"),
value = comment_value$comment,
height = '300px',
width = '300px')
})
# Generate the submitted text display
output$commenttext <-
renderText({
comment_value$submit
})
}
)
Some comments on your code
I struggled a little with determining what your code was doing. Part of the reason was that your server function was organized a bit chaotically. Your components are
output
reactive list
observer
output (but not assigned to a slot...superfluous)
reactive object
output
I'd recommend grouping your reactives together, your observers together, and your outputs together. If you have truly separate systems, you can break the systems into different sections of code, but have them follow a similar pattern (I would claim that these two boxes are part of the same system)
Your commentUi reactive has a strange if-else construction. It always sets com to a random string. What's more, the if-else construction isn't really necessary because no where in your code do you ever update comment_value$default--it is always 0. It looks like you may have been trying to base this off of an action button at some point, and then concluded (rightly) that that wasn't a great option.
Also, I would advise against building UI components in your reactive objects. You'll find your reactives are much more flexible and useful if they return values and then build any UI components within the render family of functions.
This is my day one with R shiny and I'm trying to figure out the following question: assuming that I have a root directory like
/usr/data/
This directory might contain some folders (A,B,C,...) where each folder has contains some files (no folders within them and just files). I am planning to create a dropdown menue based on another dropdown menu. The use select a folder name from the list and the second dropdown menu is automatically loaded with the file names within that folder. For example if folder A is selected and it contains File1 and File2 then the second dropdown will contain those two. This is how I'm doing it right now:
ui.R
library(shiny)
shinyUI(fluidPage(
titlePanel("This is a test app"),
sidebarLayout(
sidebarPanel(
uiOutput("Box1"),
uiOutput("Box2")
),
mainPanel("Display results",
tableOutput("view"))
)
))
server.R
library(shiny)
biz = data.frame(
Folder = c("A", "A", "B" , "B"),
Filename = c("File1","File2","File3","File4"),
stringsAsFactors = FALSE
)
shinyServer(function(input, output) {
output$Box1 = renderUI(selectInput("folder","Select directory",c("None",unique(biz$Folder)),"None"))
output$Box2 = renderUI(
if (is.null(input$folder) || input$folder == "None"){return()
}else selectInput("filename",
"Select dataset",
c("None",unique(biz$Filename[which(biz$Folder == input$folder)])),
"None")
)
subdata1 = reactive(biz[which(biz$Folder == input$folder),])
subdata2 = reactive(subdata1()[which(subdata1()$Filename == input$filename),])
output$view = renderTable({
if(is.null(input$folder) || is.null(input$filename)){return()
} else if (input$folder == "None" || input$filename == "None"){return()
} else return(subdata2())
})
})
As you can see, I'm using a dataframe to take care of that but this should better be dynamic in case that the content of those folders change. So I think the best way is to have list of folder names and then get the contents of that folder and load them in to the second dropdown. I have tried several posts on SO but they are mostly about the content of a csv file and so on. Any help would be much appreciated.
Thanks
I recommend generating UI elements reactively in the following way:
ui.R
library(shiny)
shinyUI(fluidPage(
titlePanel("My Great File Selector"),
fluidRow(
sidebarPanel(
uiOutput("select.folder"),
uiOutput('select.file')
)
)
))
server.R
library(shiny)
shinyServer(function(input, output) {
root <- '~'
output$select.folder <-
renderUI(expr = selectInput(inputId = 'folder.name',
label = 'Folder Name',
choices = list.dirs(path = root,
full.names = FALSE,
recursive = FALSE)))
output$select.file <-
renderUI(expr = selectInput(inputId = 'file.name',
label = 'File Name',
choices = list.files(path = file.path(root,
input$folder.name))))
})
All I have done in ui.R is essentially promise that I will render two UI elements named 'select.folder' and 'select.file' in server.R. That's pretty simple.
In server.R, I specify root, where I want the app to look for directories.
I use renderUI() to generate a UI element in which the user will select the folder and store it in output$select.folder, fulfilling my first promise in ui.R, give it a label of 'Folder Name', and set its choices to the result of list.dirs() (from base R) called with the appropriate arguments. The choice that the user has selected in this UI element will be accessible via input$folder.name because the InputId is 'folder.name'.
I use renderUI() again to generate a UI element that will be used to select a file in that folder and store it in output$select.file, fulfilling my second promise from ui.R. Its choices are based on a call to list.files that uses the folder selected in the first UI element, retrieving it via `input$folder.name'.
You can get the names of the files in a folder with the list.files function.
So something like this should work:
ROOT <- '/usr/data'
dropdown_options <- list.files(paste(ROOT, input$folder, sep = '/'))
In shiny, how do you use tagList inside renderUI to create multiple widgets customized with data from an uploaded file? This idea is referenced here, but there doesn't appear to be very good documentation for tagList.
I plan to answer my own question here. I did a bit of research, found that a simple example of this process was lacking, and felt a desire to contribute it so that others might benefit.
In server.R, define an object using a reactive() statement to hold the contents of the uploaded file. Then, in a renderUI statement, wrap a comma-delimited list of widget definitions in a tagList function. In each widget, use the object holding the contents of the uploaded file for the widget parameters. The example below, hosted at shinyapps.io and available on github, creates a checkBoxGroupInput and a radioButtons widget using a singer renderUI that is defined based on an uploaded file.
server.R
library(shiny)
shinyServer(function(input, output) {
ItemList = reactive(
if(is.null(input$CheckListFile)){return()
} else {d2 = read.csv(input$CheckListFile$datapath)
return(as.character(d2[,1]))}
)
output$CustomCheckList <- renderUI({
if(is.null(ItemList())){return ()
} else tagList(
checkboxGroupInput(inputId = "SelectItems",
label = "Which items would you like to select?",
choices = ItemList()),
radioButtons("RadioItems",
label = "Pick One",
choices = ItemList(),
selected = 1)
)
})
})
ui.R
library(shiny)
shinyUI(fluidPage(
titlePanel("Create a checkboxGroupInput and a RadioButtons widget from a CSV"),
sidebarLayout(
sidebarPanel(fileInput(inputId = "CheckListFile", label = "Upload list of options")),
mainPanel(uiOutput("CustomCheckList")
)
)
))
I am new to R and Shiny and I have some data files stored on my Shiny server.
What I want to do is to choose a file based upon user selection and then read that file into a data frame.
I am currently getting an object not found error, although the name is being transferred properly to the server UI.
Here is some code, fisrt server.r
library(shiny)
library(datasets)
filenames<-list.files(path="~/qc",pattern="\\.csv$")
shinyServer(function(input,output){
output$choose_dataset<-renderUI({
selectInput("dataset","Data set",filenames)
})
output$data_table<-renderTable({
selFile<-get(input$dataset)
mydat<-read.csv(selFile$name,header=T)
head(mydat,50)
})
})
Here is the ui.r
library(shiny)
shinyUI(pageWithSidebar(
headerPanel(
"Files Selection"
),
sidebarPanel(
uiOutput("choose_dataset")
),
mainPanel(
tabsetPanel(
tabPanel("plot",plotOutput("plot"),id="myplot"),
tabPanel("Data",tableOutput("data_table"),id="myTab"),
id="Plot_Data"
)
)
))
Thanks for helping.
I made a few small changes and it works for me. Try and see this works for you as well.
In server.R I moved the selectInput() to UI.R, and I moved the filenames variable to UI.R as well.
Now, since input$dataset in a file, you don't get the get() command.
Server.R
library(shiny)
library(datasets)
shinyServer(function(input,output){
output$data_table<-renderTable({
#selFile<-get(input$dataset)
mydat<-read.csv(input$dataset, header=T)
head(mydat,50)
})
})
The updated UI.R
library(shiny)
filenames<-list.files(pattern="\\.csv$")
shinyUI(pageWithSidebar(
headerPanel(
"Files Selection"
),
sidebarPanel(
selectInput(inputId = "dataset",
label = "Choose Dataset",
filenames
)
),
mainPanel(
tabsetPanel(
tabPanel("plot",plotOutput("plot"),id="myplot"),
tabPanel("Data",tableOutput("data_table"),id="myTab"),
id="Plot_Data"
)
)
))
Try this and you shouldn't be getting the object not found error. You can use these files as the basis and build on top of these.
Hope this helps.