Shiny checkboxGroupInput with Choices Generated From ggplot - html

I'm trying to create a checkbox for which the choices are plots created through ggplot. In the result, the UI looks like the HTML code itself instead of evaluating the HTML code to show the chart. Any ideas how I can get the checkboxGroupInput to show ggplots?
Sample code below -
runApp(shinyApp(
ui = fluidPage(
headerPanel("Plot check box"),
mainPanel(
uiOutput("plotscheckboxes")
)
),
server = function(input, output, session) {
output$plot1 = renderPlot({
ggplot(mtcars)+geom_point(aes(x=mpg,y=mpg))
})
output$plot2 = renderPlot({
ggplot(mtcars)+geom_point(aes(x=mpg,y=mpg))
})
output$plotscheckboxes = renderUI({
plotlist = list(
plotOutput('plot1'),
plotOutput('plot2')
)
plotlist2 = do.call(tagList, plotlist)
# this just produces a list with 1,2, some sort of underlying value for the checkboxGroup
finaloptionlist = lapply(
seq(length(plotlist2)),
function(x) x
)
# the names of the list are what get used in the options so setting the names accordingly as the HTML code of the ggplot rendering
names(finaloptionlist) = sapply(plotlist2, function(x) paste(x, collapse = "\n"))
checkboxGroupInput("checkGroup", label = h3("Checkbox group"),
choices = finaloptionlist,
selected = 1)
})
}
))

Related

How do I get output value in server based on selected input value in ui?

I am trying to create a shiny app that works like a look up table -- I am using multiple columns from my data frame as input variables in the sidebar and based on the inputs the user selects from the dropdown, I am trying to get a corresponding output for two variables (one numeric and one character) which exist in the same table.
However, when I try to link my input to get the matching output in the server code, I get the following error for my numeric output variable: "Warning: Error in writeImpl: Text to be written must be a length-one character vector" and the following error for my character output variable: "operations are possible only for numeric, logical or complex types".
I need help in resolving this, thank you! I have attached my simplified code and data with two input and two output variables for reference.
This is my data:
"input1","input2","NumericOutput","CharacterOutput"
"precarious","precarious",0,"precarious"
"precarious","precarious",2.950337429,"precarious"
"precarious","precarious",4.827824883,"precarious"
"precarious","precarious",8.314587299,"precarious"
"precarious","precarious",7.276345388,"precarious"
"precarious","precarious",10.22668282,"precarious"
"precarious","precarious",12.10417027,"precarious"
"precarious","precarious",15.59093269,"precarious"
"precarious","precarious",0.622945146,"precarious"
"precarious","precarious",3.573282575,"precarious"
"precarious","precarious",5.450770029,"precarious"
"precarious","precarious",8.937532445,"precarious"
"precarious","precarious",7.899290535,"precarious"
"precarious","precarious",10.84962796,"precarious"
"precarious","precarious",12.72711542,"precarious"
"precarious","precarious",16.21387783,"precarious"
"precarious","precarious",3.737670877,"precarious"
"precarious","moderate",6.688008306,"precarious"
"good","precarious",8.565495761,"precarious"
This is my code:
## loading packages
{
library(shiny)
library(shinydashboard)
library(htmltools)
library(rvest)
library(XML)
library(measurements)
library(ggplot2)
library(ggrepel)
library(plotly)
library(shinyjs)
library(shinyWidgets)
}
Test <- read.csv("Test.csv", stringsAsFactors = FALSE)
summary(Test)
lapply(Test,class)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
useShinyjs(),
selectInput ("a",label = colnames(Test[1]),
choices = (Test[[1]])),
selectInput("b",colnames(Test[2]),
choices = 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$NumericOutput[Test$input2 == input$b]),
subtitle = NULL) })
output$info_box2 <- renderValueBox({
valueBox(value = paste0("Assessment: ",(Test$CharacterOutput[Test$input1 == input$a])&
(Test$CharacterOutput[Test$input2 == input$b])),
subtitle = NULL)
})
}
shinyApp(ui, server)
Welcome to stackoverflow! The problem with the above code is, that the choices you are providing to populate the selectInput's aren't identifying a single row of your data.frame. However, valueBox's value-argument expects a single string.
I'm not sure what your expected result is, but maybe the following helps to understand what the issue is:
## loading packages
{
library(shiny)
library(shinydashboard)
library(shinyjs)
}
Test <- data.frame(
stringsAsFactors = FALSE,
input1 = c("precarious","precarious",
"precarious","precarious","precarious","precarious",
"precarious","precarious","precarious","precarious",
"precarious","precarious","precarious","precarious",
"precarious","precarious","precarious","precarious","good"),
input2 = c("precarious","precarious",
"precarious","precarious","precarious","precarious",
"precarious","precarious","precarious","precarious",
"precarious","precarious","precarious","precarious",
"precarious","precarious","precarious","moderate",
"precarious"),
NumericOutput = c(0,2.950337429,4.827824883,
8.314587299,7.276345388,10.22668282,12.10417027,
15.59093269,0.622945146,3.573282575,5.450770029,8.937532445,
7.899290535,10.84962796,12.72711542,16.21387783,
3.737670877,6.688008306,8.565495761),
CharacterOutput = c("precarious","precarious",
"precarious","precarious","precarious","precarious",
"precarious","precarious","precarious","precarious",
"precarious","precarious","precarious","precarious",
"precarious","precarious","precarious","precarious",
"precarious")
)
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$NumericOutput[Test$input2 == input$b], collapse = ", "),
subtitle = NULL)
})
output$info_box2 <- renderValueBox({
valueBox(value = paste0(
"Assessment: ",
Test$CharacterOutput[Test$input1 == input$a],
Test$CharacterOutput[Test$input2 == input$b], collapse = ", "),
subtitle = NULL)
})
}
shinyApp(ui, server)
I basically needed an output value for both my output variables by checking all conditions and not just fulfilling any one condition.
Instead of using Test$NumericOutput[Test$input1 == input$a]
& Test$NumericOutput[Test$input2 == input$b]
I used Test$NumericOutput[Test$input1 == input$a & Test$input2 == input$b] and it worked.

Alignment of selectizeInput and numericInputs generated inside renderUI

I have an app where the user will generate a bunch of selectizeInputs along with 3 numericInputs for every selectizeInput. The problem I am having is that the selectizeInput does not align well with the numericInputs and once you have about 10 rows, the alignment is completely gone.
I have thought of two ways of solving this problem:
create one renderUI function and include fluidRows in a loop but some searching has led me to believe that isnt possible.
Height adjust the selectizeInput with using tags$style(type = "text/css", ".form-control.shiny-bound-input, .selectize-input {height: 46px;}"), but I dont want to adjust the selectizeInput height universally as the app has selectizeInputs elsewhere.
I can't really hardcode the input name with tags$style(type = "text/css", "#some_id.form-control.shiny-bound-input {height: 46px;}") since the names are dynamically generated by the user.
Will one of these two options work? If not is there a third option?
I have made a demo version of the problem below.
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(width = 3, br(),br(),br(),br(),br(),br(),br(), h6("something else is here")),
mainPanel(
tabsetPanel(
tabPanel("Problem tab",
br(),
numericInput("inputs_num","Enter Number of Channels to Calibrate", min = 1, value = 10),
hr(),
br(),
fluidRow(
column(width= 3,uiOutput("colname")),
column(width =3, uiOutput("initial_numeric")),
column(width =3, uiOutput("min_numeric")),
column(width =3, uiOutput("max_numeric"))
),
hr()
)
)
)
)
)
server <- function(input, output, server){
output$colname <- renderUI({
req(input$inputs_num)
columns <- colnames(mtcars)
tags <- tagList()
for(i in 1:input$inputs_num){
tags[[i]] = selectizeInput(paste0("colname_",i), paste0("Column ",i), choices = columns, selected = NULL,
options = list(
placeholder = "Enter Column Name",
onInitialize = I('function() { this.setValue(""); }')
))
}
tags
})
output$initial_numeric <- renderUI({
req(input$inputs_num)
tags <- tagList()
for (i in 1:input$inputs_num){
tags[[i]] <- numericInput(paste0("initial_",i), paste("Initial",i), min = 0,value = 1)
}
tags
})
output$min_numeric <- renderUI({
req(input$inputs_num)
tags <- tagList()
for (i in 1:input$inputs_num){
tags[[i]] <- numericInput(paste0("min_",i), paste("Min",i), min = 0,value = 1)
}
tags
})
output$max_numeric <- renderUI({
req(input$inputs_num)
tags <- tagList()
for (i in 1:input$inputs_num){
tags[[i]] <- numericInput(paste0("max_",i), paste("Max",i), min = 0,value = 1)
}
tags
})
}
shinyApp(ui, server)
In Safari everything was aligned fine, and only in Firefox it became visible. I tried wrapping everything in one loop and it seems to work fine, even in Firefox.
So the approach below should correspond to solution 1. Since solution 2 (changing the css of the input universally) is not an option, another approach would be to define custom inputs by wrapping the original inputs in a tag and adding an additional class which can then be targeted in css. But I think that this not necessary, since the approach below works.
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(width = 3, br(),br(),br(),br(),br(),br(),br(), h6("something else is here")),
mainPanel(
tabsetPanel(
tabPanel("Problem tab",
br(),
numericInput("inputs_num","Enter Number of Channels to Calibrate", min = 1, value = 10),
hr(),
br(),
uiOutput("all"),
hr()
)
)
)
)
)
server <- function(input, output, server){
output$all <- renderUI({
req(input$inputs_num)
columns <- colnames(mtcars)
tags <- tagList()
for(i in 1:input$inputs_num){
tags[[i]] <- fluidRow(
column(width= 3,
selectizeInput(paste0("colname_",i), paste0("Column ",i), choices = columns, selected = NULL,
options = list(
placeholder = "Enter Column Name",
onInitialize = I('function() { this.setValue(""); }')
))),
column(width= 3,
numericInput(paste0("initial_",i), paste("Initial",i), min = 0,value = 1)),
column(width= 3,
numericInput(paste0("min_",i), paste("Min",i), min = 0,value = 1)),
column(width= 3,
numericInput(paste0("max_",i), paste("Max",i), min = 0,value = 1))
)
}
tags
})
}
shinyApp(ui, server)

R Shiny - Editing a data table inside a dynamically created bsModal

The app below contains an actionButton Add data that inserts a UI element each time it is clicked. Each UI element is a box that contains one selectInput Select data and an actionButton Edit that opens a modal when clicked.
Each modal contains:
A data table with two columns: Parameter and Value (this column is editable).
An actionButton Apply, which applies any changes made to the Value
column.
When the user selects a dataset inside box x, a reactiveValue is created to store the corresponding parameters in a data.frame x_paramset (where x is the id of the box inserted via insertUI) and add a val column which has the same value as default (see list at the top of code below). I then use renderDataTable to add the Value column (which contains the numericInput) - this data table is displayed inside the modal.
To update the data.frame to apply any changes the user may have made in the modal, I use an observeEvent that listens for the Apply button and updates the val column in the data.frame x_paramset with the values inside the numericInputs in the Value column.
Here is the app (the bsModal has been commented out and replaced with a shinyWidgets::dropdownButton):
library(shiny)
library(shinydashboard)
library(shinyjs)
library(shinyWidgets)
library(DT)
library(tidyverse)
all = list(p1 = list(a = list(id = "a", default = 10)),
p2 = list(x = list(id = "x", default = 20)))
# UI ----------------------------------------------------------------------
ui<-fluidPage(shinyjs::useShinyjs(),
tags$head(
tags$script("
$(document).on('click', '.dropdown-shinyWidgets li button', function () {
$(this).blur()
Shiny.onInputChange('lastClickId',this.id)
Shiny.onInputChange('lastClick',Math.random())
});
")
),
box(title = "Add data",
column(width = 12,
fluidRow(
tags$div(id = 'add')
),
fluidRow(
actionButton("addbox", "Add data")
))
)
)
# SERVER ------------------------------------------------------------------
server <- function(input, output, session) {
rvals = reactiveValues()
getInputs <- function(pattern){
reactives <- names(reactiveValuesToList(input))
name = reactives[grep(pattern,reactives)]
}
observeEvent(input$addbox, {
lr = paste0('box', input$addbox)
insertUI(
selector = '#add',
ui = tags$div(id = lr,
box(title = lr,
selectizeInput(lr, "Choose data:", choices = names(all)),
shinyWidgets::dropdownButton(inputId = paste0(lr, "_settings"),
circle = F, status = "success", icon = icon("gear"), width = "1000px",
tooltip = tooltipOptions(title = "Click to edit"),
tags$h4(paste0("Edit settings for Learner", lr)),
hr(),
DT::dataTableOutput( paste0(lr, "_paramdt") ),
bsButton(paste0(lr, "_apply"), "Apply")
) # end dropdownButton
)
) #end tags$div
) # end inserUI
# create reactive dataset
rvals[[ paste0(lr, "_paramset") ]] <- reactive({
do.call(rbind, all[[ input[[lr]] ]]) %>%
cbind(., lr) %>%
as.data.frame %>%
mutate(val = default)
}) # end reactive
# render DT in modal
output[[ paste0(lr, "_paramdt") ]] <- renderDataTable({
DT <- rvals[[ paste0(lr, "_paramset") ]]() %>%
mutate(
Parameter = id,
Value = as.character(numericInput(paste0(lr,"value",id), label = NULL, value = default))) %>%
select(Parameter:Value)
datatable(DT,
selection = 'none',
#server = F,
escape = F,
options = list(preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node()); }'),
drawCallback = JS('function() { Shiny.bindAll(this.api().table().node()); } ')))
}) # end renderDT
# Apply changes
observeEvent(input$lastClick, {
# replace old values with new
rvals[[ paste0(lr, "_paramset") ]](rvals[[ paste0(lr, "_paramset") ]]() %>%
mutate(
val = input$box1valuea
)
)
}) # end apply changes observeEvent
}) #end observeEvent
}
shinyApp(ui=ui, server=server)
I encounter errors when I try the following:
Add data >> Edit >> make some change to numericInput >> Apply - this
resets the numericInput inside the modal back to its default whereas
I would like the user-specified value to persist upon applying
changes or closing the modal.
The app crashes when I try:
Add data >> Edit >> Apply >> close modal >> Add data OR
Click Add data twice and then click Edit in either box.
I am not sure where my server logic is failing. I know Shiny does not support "persistent use" modals (https://github.com/rstudio/shiny/issues/1590) but I was wondering if there was a workaround? I am also not sure what inside the insertUI observeEvent is causing the app to crash in the cases described above. Any help you can offer would be greatly appreciated!

Render HTML on Shiny tabpanel

The HTML output is created by summarytool::dfSummary function.
summarytools
summarytools uses Bootstrap’s stylesheets to generate standalone HTML documents that can be displayed in a Web Browser or in RStudio’s Viewer using the generic print() function.
When the HTML gets rendered on the tabpanel, the whole UI changes. Is there a way to render the HTML on the tabpanel without changing the UI?
library(summarytools)
ui <- fluidPage(
titlePanel("dfSummary"),
sidebarLayout(
sidebarPanel(
uiOutput("dfSummaryButton")
),
mainPanel(
tabsetPanel(
tabPanel("Data Input",
dataTableOutput("dataInput"),
br(),
verbatimTextOutput("profileSTR")),
tabPanel("dfSummary Output",
htmlOutput("profileSummary")))
)
)
)
server <- function(input, output, session) {
#Read in data file
recVal <- reactiveValues()
dfdata <- iris
#First 10 records of input file
output$dataInput <- renderDataTable(head(dfdata, n = 10), options = list(scrollY = '500px',
scrollX = TRUE, searching = FALSE, paging = FALSE, info = FALSE,
ordering = FALSE, columnDefs = list(list(className = 'dt-center',
targets = '_all'))))
#str() of input file
output$profileSTR <- renderPrint({
ProStr <- str(dfdata)
return(ProStr)
})
#Create dfSummary Button
output$dfSummaryButton <- renderUI({
actionButton("dfsummarybutton", "Create dfSummary")
})
### Apply dfSummary Buttom
observeEvent(input$dfsummarybutton, {
recVal$dfdata <- dfdata
})
#dfSummary data
output$profileSummary <- renderUI({
req(recVal$dfdata)
SumProfile <- print(dfSummary(recVal$dfdata), omit.headings = TRUE, method = 'render')
SumProfile
})
}
shinyApp(ui, server)
Version 0.8.3 of summarytools has a new boolean option, bootstrap.css which will prevent this from happening. Also, graph.magnif allows adjusting the graphs' size.
SumProfile <- print(dfSummary(recVal$dfdata),
method = 'render',
omit.headings = TRUE,
footnote = NA,
bootstrap.css = FALSE,
graph.magnif = 0.8)
The latest version can be installed with devtools:
devtools::install_github("dcomtois/summarytools")
Good luck :)

selectInput in R shiny

I want to select from a list read in from a Mysql query. I am getting an error in the code. I must be doing something just completely wrong, but not sure what.
I would like to select from a list of skus read in from a sql query. I get an error in the ui portion.
I am not even sure if this is possible, but to list out all skus would be extremely timely.
I am getting the following errors:
Error in tag("div", list(...)) :
argument "sidebarPanel" is missing, with no default
shinyApp(ui = ui, server = server)
Error in force(ui) : object 'ui' not found
library('RMySQL')
library('plyr')
library('shiny')
library('scales')
library(shinyapps)
library(ggplot2)
con <- dbConnect(MySQL(), user="user", password="password",dbname="DB", host="host");
rank<-dbGetQuery(con,"select sku from DB")
#build a shiny app to select which sku to pick
server <- function(input, output) {
output$distPlot <- renderPlot({
hist(rnorm(input$obs), col = 'darkgray', border = 'white')
})
}
ui <- pageWithSidebar(
## Application title
sidebarPanel(
sliderInput("obs", "Number of observations:", min = 10, max = 500, value = 100),
selectInput(
'e0', '0. An ordinary select input', choices = unique(rank$sku),
selectize = FALSE
),
mainPanel(plotOutput("distPlot"))
)
)
shinyApp(ui = ui, server = server)
You have both a missing bracket near your selectize = FALSE and (as #DavidRobinson has suggested) you need a headerPanel.
CODE FIX
library(shiny)
library(ggplot2)
# con <- dbConnect(MySQL(), user="user", password="password",dbname="DB", host="host");
# rank<-dbGetQuery(con,"select sku from DB")
# for test hard coding the rank as I dont have your data
# test rank
rank$sku <- c(1,2,3)
#build a shiny app to select which sku to pick
server <- function(input, output) {
output$distPlot <- renderPlot({
hist(rnorm(input$obs), col = 'darkgray', border = 'white')
})
}
ui <- pageWithSidebar(
## Application title
# missing headerPanel
headerPanel(title = "Hello"),
# missing bracket after selectize
sidebarPanel(
sliderInput("obs", "Number of observations:", min = 10, max = 500, value = 100),
selectInput(
'e0', '0. An ordinary select input', choices = unique(rank$sku),
selectize = FALSE)
),
mainPanel(plotOutput("distPlot"))
)
shinyApp(ui = ui, server = server)
RESULT
ANOTHER SHINY PAGE UI OPTION
You can can also use a tabbed page structure, replacing ui above with this code (note it does not require headerPanel like above):
# navbar tabbed page example - without headerPanel
ui2 <- navbarPage(title = "Hello Another Style",
tabPanel("Chart Panel",
sidebarLayout(
sidebarPanel(
sliderInput("obs", "Number of observations:",
min = 10, max = 500, value = 100),
selectInput(
'e0', '0. An ordinary select input',
choices = unique(rank$sku),
selectize = FALSE)
),
mainPanel(
plotOutput("distPlot")
)
)
),
tabPanel("Instructions",
mainPanel(
p("Notes here for example...")
)
)
)
SECOND RESULT
And then on second panel...
DEBUGGING ADVICE
These Shiny pages can have lots of brackets, so pace over your code selecting brackets in turn carefully in your editor like RStudio to make sure your brackets match okay.
All the best!