Shiny Apps: is it possible to render HTML in validation messages? - r

Is there any way to render HTML in shiny's validation messages? I tried different approaches using the HTML wrapper, the tags$... functions, as well as a separate htmlOutput for the validation message, but could not get any of them to work. Here is a simple example app that shows this issue - the select should be bold in the validation message but the HTML tags are escaped (contrived example, I know, but hopefully conveys the idea, I would primarily like to use this to include fa icons in the messages):
runApp(
list(
ui = fluidPage(
titlePanel("Validation App"),
sidebarLayout(
sidebarPanel(
selectInput("data", label = "Data set", choices = c("", "mtcars"))
),
mainPanel(tableOutput("table"))
)
),
server = function(input, output) {
data <- reactive({
# validate test
validate(
need(input$data != "", HTML("Please <strong>select</strong> a data set"))
)
get(input$data, 'package:datasets')
})
output$table <- renderTable(head(data()))
}
)
)

The simplest solution is to use a uiOutput and inside the renderUI function put an if to validate the input. In the code below is an example using HTML and tags$... functions. You can can also put an icon.
library(shiny)
runApp(
list(
ui = fluidPage(
titlePanel("Validation App"),
sidebarLayout(
sidebarPanel(
selectInput("data", label = "Data set", choices = c("", "mtcars"))
),
mainPanel(uiOutput("tableUI"))
)
),
server = function(input, output) {
data <- reactive({
get(input$data, 'package:datasets')
})
output$tableUI <- renderUI({
if (input$data == "") {
div(
HTML("Please <strong>select</strong> a data set"),
tags$p(icon("exclamation"), "Please",tags$strong("select"), "a data set")
)
} else {
tableOutput("table")
}
})
output$table <- renderTable(head(data()))
}
)
)

Related

How to have a user input text and create a list with shiny? R

I have the following app which allows for text to be entered and it is then saved as VALUE and printed on a panel.
Although it looks like I can only do this with one text input at a time - even if I click add (so I don't believe this button is working). On top of that I would like for the user to be able to add multiple inputs (like I have below).
And then my VALUE function should be list with multiple inputs.
code below
library(shiny)
ui <- fluidPage(
headerPanel("R Package App"),
sidebarPanel(
# selectInput("options", "options", choices=c('abc','def')),
textInput("textbox", "Enter R Package Name", ""),
actionButton("add","Add")
),
mainPanel(
textOutput("caption")
)
)
server <- function(input, output, session) {
observe({
VALUE <- ''
if(input$add>0) {
isolate({
VALUE <- input$textbox
})
}
updateTextInput(session, inputId = "textbox", value = VALUE)
})
output$caption <- renderText({
input$textbox
})
}
shinyApp(ui = ui, server = server)
Have you considered using selectizeInput with it's create option?
library(shiny)
packagesDF <- as.data.frame(installed.packages())
ui <- fluidPage(
headerPanel("R Package App"),
sidebarPanel(
selectizeInput(
inputId = "selectedPackages",
label = "Enter R Package Name",
choices = packagesDF$Package,
selected = NULL,
multiple = TRUE,
width = "100%",
options = list(
'plugins' = list('remove_button'),
'create' = TRUE,
'persist' = TRUE
)
)
),
mainPanel(textOutput("caption"))
)
server <- function(input, output, session) {
output$caption <- renderText({
paste0(input$selectedPackages, collapse = ", ")
})
}
shinyApp(ui = ui, server = server)

Display a selected image from upload in Shiny UI

I want to be able to upload multiple images with file input and display the single image selected in the UI
ui.R
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput("file","Upload the file", multiple = TRUE), # fileinput() function is used to get the file upload contorl option
uiOutput("selectfile")
),
mainPanel(
uiOutput('images')
)
)
)
server.R
server <- function(input,output) {
## Side bar select input widget coming through renderUI()
# Following code displays the select input widget with the list of file loaded by the user
output$selectfile <- renderUI({
if(is.null(input$file)) {return()}
list(hr(),
helpText("Select the files for which you need to see data and summary stats"),
selectInput("Select", "Select", choices=input$file$name)
)
})
output$images <- renderImage({
if(is.null(input$file)) {return(NULL)}
for (i in 1:nrow(input$file))
{
if(input$file$name[i] == input$Select){
list(src=input$file$datapath[i],
alt= "error")
print(input$file$name[i])
print(input$file$datapath[i])
}
}
})
}
With this solution, the prints of the datapath and the name shows me the right answer but i keep getting the same error after trying to render the image: "Warning: Error in basename: a character vector argument expected".
Here is a solution using base64 encoding.
library(shiny)
library(base64enc)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput("file", "Upload the file", multiple = TRUE),
uiOutput("selectfile")
),
mainPanel(
uiOutput('image')
)
)
)
server <- function(input,output) {
output$selectfile <- renderUI({
req(input$file)
list(hr(),
helpText("Select the files for which you need to see data and summary stats"),
selectInput("Select", "Select", choices=input$file$name)
)
})
output$image <- renderUI({
req(input$Select)
i <- which(input$file$name == input$Select)
if(length(i)){
base64 <- dataURI(file = input$file$datapath[i], mime = input$file$type[i])
tags$img(src = base64, alt= "error")
}
})
}
shinyApp(ui, server)

Using Conditionalpanel Function in Shiny

I'm trying to create the scenario whereby using conditionalpanel, I am able to have an user input of checked boxes to display either 1 or 2 plots, one after another.
My reproducible code can be found below, however, I am unable to display the plots.
Could someone please share with me where did I make a mistake?
library(shiny)
ui = fluidPage(
titlePanel("Plot1 or Plot2?"),
sidebarLayout(
sidebarPanel(
checkboxGroupInput("my_choices", "Plot1 or Plot2",choices = c("Plot1", "Plot2"), selected = "Plot1"),width=2),
mainPanel(
conditionalPanel(
condition = "input.my_choices == 'Plot1'",
plotOutput("plot1")
),
conditionalPanel(
condition = "input.my_choices == 'Plot2'",
plotOutput("plot2")
),
conditionalPanel(
condition = "input.my_choices.includes('Plot1', 'Plot2')",
plotOutput("plot1"),
plotOutput("plot2")
)
)
)
)
server = function(input, output) {
output$plot1 <- renderPlot({plot(iris)})
output$plot2 <- renderPlot({plot(mtcars)})
}
shinyApp(ui, server)
Update:
I've got what I wanted but without using ConditionalPanel function. Here's the code below:
Would appreciate if someone can share with me the proper way of using ConditionalPanel Function! (:
library(shiny)
#data
df <- iris
#ui
ui <- fluidPage(
sidebarPanel(
checkboxGroupInput(inputId = "Question",
label = "Choose the plots",
choices = c("Plot1", "Plot2", "Plot3"),
selected = "")),
mainPanel(
uiOutput('ui_plot')
)
)
#server
server <- function(input, output)
{
# gen plot containers
output$ui_plot <- renderUI({
out <- list()
if (length(input$Question)==0){return(NULL)}
for (i in 1:length(input$Question)){
out[[i]] <- plotOutput(outputId = paste0("plot",i))
}
return(out)
})
# render plots
observe({
for (i in 1:3){
local({ #because expressions are evaluated at app init
ii <- i
output[[paste0('plot',ii)]] <- renderPlot({
if ( length(input$Question) > ii-1 ){
return(plot(runif(100)))
}
NULL
})
})
}
})
}
shinyApp(ui, server)
I would give you an alternative as you will need to create new plots with different id in order for that to work. The simplest one I can think of is using shinyjs package and its hide and show functions. You can also do this via renderUI but you shouldn't give unnecessary work to your server only if you're showing and hiding the elements
library(shiny)
library(shinyjs)
ui = fluidPage(
useShinyjs(),
titlePanel("Plot1 or Plot2?"),
sidebarLayout(
sidebarPanel(
checkboxGroupInput("my_choices", "Plot1 or Plot2",choices = c("Plot1", "Plot2"), selected = "Plot1"),width=2),
mainPanel(
plotOutput("plot1"),
plotOutput("plot2")
)
)
)
server = function(input, output,session) {
# hide plots on start
hide("plot1");hide("plot2")
output$plot1 <- renderPlot({plot(iris)})
output$plot2 <- renderPlot({plot(mtcars)})
observeEvent(input$my_choices,{
if(is.null(input$my_choices)){
hide("plot1"); hide("plot2")
}
else if(length(input$my_choices) == 1){
if(input$my_choices == "Plot1"){
show("plot1");hide("plot2")
}
if(input$my_choices == "Plot2"){
hide("plot1");show("plot2")
}
}
else{
if(all(c("Plot1","Plot2") %in% input$my_choices)){
show("plot1");show("plot2")
}
}
},ignoreNULL = F)
}
shinyApp(ui, server)

Using input to create UI in Shiny R

I am building an app in shiny (R). At the beginning the user can upload a file to use (I am doing a sort data analysis). My goal is to be able to use files without knowing how many columns this file has, and how the data exactly looks like.
So now I have to select the columns by number, and I made a small preview app for this to select columns and then display them next to the original:
library(shiny)
ui <-fluidPage(
headerPanel("Select data"),
sidebarLayout(
sidebarPanel(
fileInput("uploadFile", "XLSX file"),
textInput('vec1', 'Choose training columns', "3,4"),
actionButton("choose","choose data")
),
mainPanel(
fluidRow(
column(6,tableOutput("data_raw")),
column(6,tableOutput("data_selected"))
)
)
)
)
server <- function(input, output) {
output$data_raw <- renderTable({
inFile <- input$uploadFile
if (is.null(inFile))
return(NULL)
data_raw <<-read.xlsx(inFile$datapath, 1)
})
observe({
if(input$choose>0){
selectvec <- as.numeric(unlist(strsplit(input$vec1,",")))
output$data_selected <- renderTable(
data_selected<- data_raw[,selectvec]
)
}
})
}
shinyApp(ui,server)
Now I would like to be able to select the columns to use on basis of their header.
It feels unnatural: changing the app while running.. but in a reactive environment.. why not?
QUESTION: How can I change the UI while it is allready running, with values originating from the input?
kind regards,
Pieter
To make me feel not as dirty for answering this...I didn't debug or handle reactives properly. But here ya go. You need to respond to the file that is uploaded on the server side, extract the column names, and append thosed to the choices in a select input that then passes down to the table function as a column filter.
upload_app <- function(){
library(shiny)
ui <- bootstrapPage(
tags$div(class = "container",
column(3,
fluidRow(
fileInput(inputId = 'user_data',
label = 'Upload Data (csv)',
multiple = FALSE,
accept = c(
'text/csv',
'text/comma-separated-values',
'text/tab-separated-values',
'text/plain',
'.csv',
'.tsv'
))
),
fluidRow(
uiOutput('column_vars')
)
),
column(9,
tableOutput('filtered_table'))
)
)
server <- function(session, input, output){
var_table <- reactive({
var_data <- input$user_data
read.csv(var_data$datapath, header = TRUE,sep = ",", quote = '')
})
output$column_vars <- renderUI({
if(!is.null(var_table())){
selectInput(inputId = 'cols',
choices = colnames(var_table()),
multiple = T,
label = "Choose Columns")
}
})
output$filtered_table <- renderTable({
if(!is.null(var_table())){
if(length(input$cols)>0){
get_these <- input$cols
new_table <- var_table()[,c(get_these)]
}else {
new_table <- var_table()
}
}else {
new_table <- data.frame(data = 'Waiting')
}
return(new_table)
})
}
shinyApp(ui, server)
}

R shiny - stop() in eventReactive without error message in console

I just new in Shiny, and i have problem. i have a event reactive and the stop function inside. when I run my code(no checkbox and do click button), the shiny is work well. but in console display the error message "eventReactiveHandler". do you have a solution for my problem? i want to no error message in my console.
and i not expect the solution is
opt <- options(show.error.messages=FALSE)
on.exit(options(opt))
because the error will not display in my all code, i want just specifically in this error.
thank you... this is the code...
rm(list = ls())
library(shiny)
library(shinyBS)
var.x<-reactiveValues()
shinyApp(
ui =
fluidPage(
sidebarLayout(
sidebarPanel(
checkboxGroupInput("indepvar","Independent Variable",
choices = c("1"=1,"2"=2)),
actionButton("tabBut", "View Table")
),
mainPanel(
uiOutput("coba"),
uiOutput("popup4")
)
)
),
server =
function(input, output, session) {
output$coba <- renderUI({
gendata()
indep<-NULL
for(i in 1:length(var.x)){
indep <- paste(indep,var.x[i],",")
}
list(
renderText(indep)
)
})
gendata<- eventReactive(input$tabBut,{
if(is.null(input$indepvar)){
stop()
}
var.x<<- input$indepvar
})
output$popup4 <- renderUI({
if(!is.null(input$indepvar))return()
list(
bsModal("modalExample4", "Peringatan", "tabBut", size = "small",wellPanel(
"Anda belum memilih independent variabel..."
))
)
})
}
)
I wouldn't advise suppressing error messages, as there are in there for you, I suggest you look into validate and need in shiny, you can go read validation article
To quickfix you issue you can just return NULL
rm(list = ls())
library(shiny)
library(shinyBS)
var.x<-reactiveValues()
shinyApp(
ui =
fluidPage(
sidebarLayout(
sidebarPanel(
checkboxGroupInput("indepvar","Independent Variable",
choices = c("1"=1,"2"=2)),
actionButton("tabBut", "View Table")
),
mainPanel(
uiOutput("coba")
)
)
),
server =
function(input, output, session) {
output$coba <- renderUI({
gendata()
indep<-NULL
for(i in 1:length(var.x)){
indep <- paste(indep,var.x[i],",")
}
list(
renderText(indep)
)
})
gendata<- eventReactive(input$tabBut,{
if(is.null(input$indepvar)){
var.x <<- NULL
return(NULL)
stop()
}
var.x<<- input$indepvar
})
}
)
You need to do two things as per the code below:
Make sure that gendata returns nothing when there are no independent variables selected (see lines 37-40). This stops your original error message
Make sure that output$coba is not evaluated when gendata has no value (see line 25)
Hope this helps,
John
rm(list = ls())
library(shiny)
library(shinyBS)
var.x<-reactiveValues()
shinyApp(
ui =
fluidPage(
sidebarLayout(
sidebarPanel(
checkboxGroupInput("indepvar","Independent Variable",
choices = c("1"=1,"2"=2)),
actionButton("tabBut", "View Table")
),
mainPanel(
uiOutput("coba"),
uiOutput("popup4")
)
)
),
server =
function(input, output, session) {
output$coba <- renderUI({
req(gendata())
indep<-NULL
for(i in 1:length(var.x)){
indep <- paste(indep,var.x[i],",")
}
list(
renderText(indep)
)
})
gendata<- eventReactive(input$tabBut,{
if(is.null(input$indepvar)) {
var.x <<- NULL
return()
}
var.x<<- input$indepvar
})
output$popup4 <- renderUI({
if(!is.null(input$indepvar))return()
list(
bsModal("modalExample4", "Peringatan", "tabBut", size = "small",wellPanel(
"Anda belum memilih independent variabel..."
))
)
})
}
)

Resources