I'm trying to make check boxes for each car model in a grouping variable, 'cylinders'.
To achieve this, I'm using lapply() to go through the groups making a groupedCheckbox in a collapsing well panel, for each.
However only some of the checked car models are read. Initially all of the checked cars in the first cylinder group can be read but not those in the second two groups !
However once some of the car models in the first group are ticked, then cars in the second two groups are also read. Image of multiple checkbox groups with only checkboxes in first box are read
MultipleGroupCheckboxProblem
Does anyone know how all of the ticked cars can be extracted into a reactive vector?
I have a feeling the solution might involve proper use of lapply and/or unlist.
Also the car names originate from the row names of the input table.
Code here, using mtcars :
library(shiny)
library(shinyBS)
ui <- fixedPage(
h2("R Shiny: Multiple collapsing tickboxes"),
tags$style(HTML("
.checkbox{margin-top: -20px; margin-left: 0px; margin-bottom: -5px;padding:-5px; margin-right: -400px;}
.panel{margin: -5px;}")),
uiOutput("grouped.tickboxes"),
textOutput("selectedtext")
)
# .panel{margin: 2px;padding:1px}")),
server <- function(input, output, session) {
output$grouped.tickboxes <- renderUI({
lapply(sort(unique(mtcars$cyl)), function(x) {
fluidRow(
div(tags$style(HTML("
.checkbox{margin: 0px; ;padding:0px; margin-right: -400px;}")),
bsCollapsePanel(paste0("Cylinders: ", x),
style = "color:grey; border-top: 1px solid black",
# style = "color:grey;",
column(12,
checkboxGroupInput(inputId = "stuff",
NULL, choices = sort(row.names(subset(mtcars, cyl %in% x))))))
)
)
})
})
seltex = reactive({
## maybe need to use lapply to read values.
# paste0(input$stuff, collapse = ", ")
# paste0(as.vector(unlist(input$stuff, use.names = FALSE)), collapse = ", ")
# as.vector(unlist(input$stuff, use.names = FALSE))[1]
# head(str(input$stuff))
# lapply(input$stuff, str(input$stuff)[2]
# paste0(unlist(unlist(unlist(input$stuff), use.names = FALSE)), collapse = ", ")
# paste0(unlist(unlist(unlist(input$stuff)), use.names = FALSE), collapse = ", ")
# paste0(unlist(unlist(input$stuff)), collapse = ", ")
paste0("Selected cars : ", paste0(unlist(input$stuff), collapse = ", "))
})
output$selectedtext = renderText({ as.character(seltex() )})
}
# grouped.tickboxes
shinyApp(ui, server)
As far as I get it the issue arises because you assign the same inputId to all three checkbox panels. Hence one approach to make your app work is to assign different inputIds for the checkbox panels. Try this:
library(shiny)
library(shinyBS)
ui <- fixedPage(
h2("R Shiny: Multiple collapsing tickboxes"),
tags$style(HTML("
.checkbox{margin-top: -20px; margin-left: 0px; margin-bottom: -5px;padding:-5px; margin-right: -400px;}
.panel{margin: -5px;}")),
uiOutput("grouped.tickboxes"),
textOutput("selectedtext")
)
# .panel{margin: 2px;padding:1px}")),
server <- function(input, output, session) {
output$grouped.tickboxes <- renderUI({
lapply(sort(unique(mtcars$cyl)), function(x) {
fluidRow(
div(tags$style(HTML("
.checkbox{margin: 0px; ;padding:0px; margin-right: -400px;}")),
bsCollapsePanel(paste0("Cylinders: ", x),
style = "color:grey; border-top: 1px solid black",
# style = "color:grey;",
column(12,
checkboxGroupInput(inputId = paste0("stuff", x),
NULL, choices = sort(row.names(subset(mtcars, cyl %in% x))))))
)
)
})
})
seltex = reactive({
cars <- purrr::reduce(sort(unique(mtcars$cyl)), ~ c(.x, input[[paste0("stuff", .y)]]), .init = character(0))
paste0("Selected cars : ", paste0(cars, collapse = ", "))
})
output$selectedtext = renderText({ seltex() })
}
# grouped.tickboxes
shinyApp(ui, server)
Related
My Shiny app suddenly is behaving n a strange manner whereby it opens briefly and then closes itself. There are no errors in the console. On my Mac, the app works fine. However, on Windows, the issue arises.
My complete code can be seen below.
Subsequently, Mac, or Windows, when I am using my full dataset .csv file, (as opposed to my small dummy test dataset), I receive an error input string 1 is invalid UTF-8. I have tried all suggestions here How to identify/delete non-UTF-8 characters in R but without any success. I have also used the CLEAN() function in Excel itself, and also tried read.csv("dummyData.csv, encoding = "UTF-8"), neither of which worked. I'm out of ideas.
Any help on both these issues would be fantastic.
library(shiny)
library(tidyverse)
library(DT)
# Reading the main_data which the shiny app depends on, Please make sure that the column names are same
main_data <- read_csv("dummyData.csv")
ui <- fluidPage(
fluidRow(column(12, tags$h2("Assignment Details"))),
sidebarLayout(
sidebarPanel(
width = 3,
tags$div(
align = "center",
tags$img(src = "logo.png", width = "120", height = "120")
),
fluidRow(
column(12, align = "center", tags$br(), tags$b("Filter data")),
column(12, selectInput("sector_filter", "Sector", unique(main_data$Sector), multiple = TRUE)),
column(12, selectInput("client_filter", "Client", unique(main_data$`Client Name`), multiple = TRUE)),
column(12, selectInput("service_filter", "Service", unique(main_data$Service), multiple = TRUE)),
column(12, selectInput("cost_filter", "Cost", unique(main_data$`Cost (Ex-Vat)`), multiple = TRUE)),
column(12, align = "center", actionLink("reset_filters", "Clear Filters/Reset", style = "color: #962693"))
)
),
mainPanel(
width = 9,
tabsetPanel(
tabPanel(
"Assignment Description",
uiOutput("assignment_description")
),
tabPanel(
"Data Table",
DTOutput("data_table")
)
)
)
)
)
server <- function(input, output, session) {
# Creating a new empty tibble (which is basically a data.frame) for filtering based on the filters selected
filtered_data <- tibble()
observeEvent(input$reset_filters, {
updateSelectInput(session, "sector_filter", selected = "")
updateSelectInput(session, "client_filter", selected = "")
updateSelectInput(session, "service_filter", selected = "")
updateSelectInput(session, "cost_filter", selected = "")
})
# The observe code block will be triggered everytime any reactive object from the UI is changed (In this case out filters)
observe({
# If all the inputs are empty, We will just send the whole data without the filters. Else we filter
print(input$sector_filter)
print(input$client_filter)
print(input$service_filter)
print(input$cost_filter)
print(unique(main_data$Sector))
sector_filter_values <- input$sector_filter
client_filter_values <- input$client_filter
service_filter_values <- input$service_filter
cost_filter_values <- input$cost_filter
if (is.null(input$sector_filter)) {
sector_filter_values <- unique(main_data$Sector)
}
if (is.null(input$client_filter)) {
client_filter_values <- unique(main_data$`Client Name`)
}
if (is.null(input$service_filter)) {
service_filter_values <- unique(main_data$Service)
}
if (is.null(input$cost_filter)) {
cost_filter_values <- unique(main_data$`Cost (Ex-Vat)`)
}
filtered_data <<- main_data %>%
filter(Sector %in% sector_filter_values, `Client Name` %in% client_filter_values,
Service %in% service_filter_values, `Cost (Ex-Vat)` %in% cost_filter_values)
# This is where the assignment description will be rendered
output$assignment_description <- renderUI({
filtered_data$title <- paste0(filtered_data$`Client Name`, " - ", filtered_data$`Assignment Name`)
HTML(
paste0(
"<br><span style='color: #962693'>", filtered_data$title,
"</span><br>", filtered_data$`Assignment Description`, "<br>"
)
)
})
# This is where the table is rendered. To customise the table visit here https://rstudio.github.io/DT/
output$data_table <- renderDT({
datatable(
filtered_data %>% select(`Client Name`, `Assignment Name`, `Sector`, `Service`, `Cost (Ex-Vat)`)
)
})
})
# Whenever a row from the table is selected the Assignment Description must change regardless the filters selected
observeEvent(input$data_table_rows_selected, {
print(input$data_table_rows_selected)
filtered_data_from_table <- filtered_data[input$data_table_rows_selected, ]
print(filtered_data_from_table)
output$assignment_description <- renderUI({
filtered_data_from_table$title <- paste0(filtered_data_from_table$`Client Name`, " - ", filtered_data_from_table$`Assignment Name`)
HTML(
paste0(
"<br><span style='color: #962693'>", filtered_data_from_table$title,
"</span><br>", filtered_data_from_table$`Assignment Description`, "<br>"
)
)
})
})
}
shinyApp(ui = ui, server = server)
I took a small modification of the top answer here:
How to identify/delete non-UTF-8 characters in R
Simply converting my columns via the below code fixed my issues.
df$`Column Name`<- iconv(df$`Column Name`, to = "UTF-8")
What I am trying to do is have the user specify the number of groups then, based on the number of groups specified, the UI generates a numericInput for each group. Then I want to use that value to do some other operations (in this example, I'm making a table of means). Using this example, I was able to make it return some text, but not use that label as input for anything else.
When I try to use that information (i.e., as reactive conductor), I get a "replacement has length zero" error. It seems shiny is not recognizing the updated UI. I know it probably has something to do with using reactive, but I can't figure out why it's not working. Here's my code:
library(shiny)
library(purrr)
# functions ---------------------------------------------------------------
## generic function that creates an input from an i
make_list = function(i, idname, labelname){
idname <- paste(idname, i, sep = "")
div(style="display: inline-block;vertical-align:top; width: 45%;",
numericInput(idname, labelname, 0))
}
## make function that can be used within a loop
list_loop = function(i) {
make_list(i, "mean", "Mean of Group ")
}
# UI ----------------------------------------------------------------------
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("A Test Page"),
sidebarLayout(
sidebarPanel(width = 8,
#### UI for groups
numericInput("groups", "How many groups?", 4),
hr(),
uiOutput("inputMean")),
# Main panel for displaying outputs ----
mainPanel(width = 4,
h3("Data Preview"),
#textOutput("inputValues"),
tableOutput("table"))
)
)
# Server ------------------------------------------------------------------
# Define server logic required to draw a histogram
server = function(input, output) {
## loop through # of groups for all i and make the UI
## this is passed back to the UI
observeEvent(input$groups,
{
output$inputMean = renderUI(
{
mean_list <- 1:input$groups %>% map(~list_loop(.x))
do.call(tagList, mean_list)
}
)
}
)
## return the inputnames
## This WORKS
output$inputValues <- renderText({
paste(lapply(1:input$groups, function(i) {
inputName <- paste("mean", i, sep = "")
input[[inputName]]
}))
})
make_table = reactive({
### prepopulate a table
d = data.frame(group = 1:input$groups)
d$means = NA
paste(lapply(1:input$groups, function(i) {
inputName <- paste("mean", i, sep = "")
# this fails because input is NULL at this point
d$means[i] = input[[inputName]]
}))
d
})
output$table <- renderTable({
make_table()
})
}
# Run the application
shinyApp(ui = ui, server = server)
If you replace your make_table with the following, it works.
I added a req that checks if all the input is present, so it won't throw errors anymore. Then, I filled d$means using the lapply you created.
make_table = reactive({
req(input$groups, input[[paste("mean", input$groups, sep = "")]])
### prepopulate a table
d = data.frame(group = 1:input$groups)
d$means = lapply(1:input$groups, function(i) {
inputName <- paste("mean", i, sep = "")
# this fails because input is NULL at this point
input[[inputName]]
})
d
})
I would like to be able to apply a function to a given set of columns from the RLdata10000 dataset. I have been going through shiny tutorials and am attempting to learn how to use observeEvent and actionButton. However, I would like to be able to pick the columns I use so I came across pickerInput. In short, I would like to be able to pick a set of columns from RLdata10000, and apply the function via actionButton.
My problem is that I get an error: Error: unused argument (RLdata10000). My code is below. I would like to be able to do this with two data files eventually. Any help would be appreciated.
library(shiny)
library(DT)
library(shinyWidgets)
library(plyr)
library(dplyr)
library(RecordLinkage)
data(RLdata10000)
cleanup <- function(x){
x <- as.character(x) # convert to character
x <- tolower(x) # make all lowercase
x <- trimws(x, "both") # trim white space
return(x)
}
ui <- basicPage(
h2("Record Linkage Data"),
actionButton(inputId = "clean", label = "Clean Data")
pickerInput(width = "75%",
inputId = "pick_col1",
label = "Select columns to display",
choices = colnames(RLdata10000),
selected = colnames(RLdata10000),
options = list(
`actions-box` = T,
`selected-text-format` = paste("count > ", length(colnames(RLdata10000)) - 1),
`count-selected-text` = "Alle",
liveSearch = T,
liveSearchPlaceholder = T
),
multiple = T)
DT::dataTableOutput("mytable")
)
server <- function(input, output) {
observeEvent(input$clean, {
output$mytable = DT::renderDataTable({
lapply(input$pick_col1, cleanup)
})
}
}
shinyApp(ui, server)
I wasn't actually able to replicate the error you noted, but you had a few issues that were preventing you from getting what (I think) you're after.
First, you were missing commas in the UI after the actionButton and pickerInput elements.
Second, you are only giving lapply the names of columns - not the data - when you use input$pick_col1, so your cleanup function has nothing to work on. Using select from dplyr provides a simple way to name the columns and get the data too.
Last, renderDataTable wants a table format as an input (i.e., either a data frame or a matrix), but lapply produces a list. You need to convert the output of lapply into a workable class.
From these three changes, updated code would look like this:
library(shiny)
library(DT)
library(shinyWidgets)
library(plyr)
library(dplyr)
library(RecordLinkage)
data(RLdata10000)
cleanup <- function(x){
x <- as.character(x) # convert to character
x <- tolower(x) # make all lowercase
x <- trimws(x, "both") # trim white space
return(x)
}
ui <- basicPage(
h2("Record Linkage Data"),
actionButton(inputId = "clean", label = "Clean Data"),
pickerInput(width = "75%",
inputId = "pick_col1",
label = "Select columns to display",
choices = colnames(RLdata10000),
selected = colnames(RLdata10000),
options = list(
`actions-box` = T,
`selected-text-format` = paste("count > ", length(colnames(RLdata10000)) - 1),
`count-selected-text` = "Alle",
liveSearch = T,
liveSearchPlaceholder = T
),
multiple = T),
DT::dataTableOutput("mytable")
)
server <- function(input, output) {
observeEvent(input$clean, {
output$mytable = DT::renderDataTable({
data.frame(lapply(select(RLdata10000, input$pick_col1), cleanup))
})
})
}
shinyApp(ui, server)
I have a shiny app with list of wellPanels. They are used in jqui_sortable from shinyjqui. Panels are generated in server part (to uiOutput in ui). Order of panels can be changed by mouse and is written to file (by ids). Then I would like to open this file and change default order with loaded data.
Issue: I can't get out of rendered words "div" between panels (run code below).
Code was written with some lines from solution (thanks to #TimTeaFan):
Distorted spacing between div elements after sorting with jqui_sortable
library(shiny)
library(shinyjqui)
ui <- fluidPage(
sidebarLayout(fluid = TRUE,
sidebarPanel(helpText("HelpText")),
mainPanel(
fluidRow(column(12,
actionButton(inputId = "btn1",label = "Button1"),
tags$style(HTML(".ui-sortable {
width: 1200px !important;
} ")),
uiOutput('multiobject'),
actionButton(inputId = "btn2",label = "Button2")
))
)
)
)
server <- function(input, output, session) {
sortableorderednameList<-reactiveVal(
c("A","B","C")
)
wpFunc <- function(v,name,helptext){
return(tags$div(wellPanel(id=paste0(v,"P"),
div(style="display: inline-block; width: 10px;",
checkboxInput(paste0(v,"Chk"), label = NULL, value = TRUE)),
div(style="display: inline-block; width: 150px;",
textInput(paste0(v,"TI"), label = NULL, value = name)),
div(style="display: inline-block;",helpText(helptext)),
style = "padding: 1px;")))
}
observe({
if(is.null(input$sortablecollistJQ_order$id)) {return()}
mylist <- input$sortablecollistJQ_order$id
mylist <- unlist(lapply(mylist, function(v) substr(v,1,nchar(v)-1)))
print(mylist)
print(" ")
isolate(sortableorderednameList(mylist))
})
output$multiobject <- renderUI({
uiList <- list()
for (v in sortableorderednameList()) {
switch(v,
"A" = {uiList <- append(uiList,wpFunc(v,"A","There is A"))},
"B" = {uiList <- append(uiList,wpFunc(v,"B","There is B"))},
"C" = {uiList <- append(uiList,wpFunc(v,"C","There is C"))}
)
}
jqui_sortable(div(id = 'sortablecollistJQ',uiList))
})
}
shinyApp(ui, server)
I have got an answer after experiments. If somebody is interested.
for (i in 1:length(uiList)) {
uiList[i] <- uiList[i]$children
}
It changes structure of list, put it before jqui_sortable call.
I'm building an app that allows user to pass the value from selectizeInput or checkboxInput to form a dataframe. I've searched a while and found these sources that similar to what I expect:
handsontable
It is from here: https://github.com/jrowen/rhandsontable. Mine is quite similar to this exampe:
shiny::runGitHub("jrowen/rhandsontable",
subdir = "inst/examples/rhandsontable_portfolio")
But I want to use shiny widgets to pass values to the dataframe. It should be able to add/remove rows as following example:
shinyIncubator
code here:
library("shiny")
library('devtools')
install_github('shiny-incubator', 'rstudio')
library("shinyIncubator")
# initialize data with colnames
df <- data.frame(matrix(c("0","0"), 1, 2))
colnames(df) <- c("Input1", "Input2")
server = shinyServer(
function(input, output) {
# table of outputs
output$table.output <- renderTable(
{ res <- matrix(apply(input$data,1,prod))
res <- do.call(cbind, list(input$data, res))
colnames(res) <- c("Input 1","Input 2","Product")
res
}
, include.rownames = FALSE
, include.colnames = TRUE
, align = "cccc"
, digits = 2
, sanitize.text.function = function(x) x
)
}
)
ui = shinyUI(
pageWithSidebar(
headerPanel('Simple matrixInput example')
,
sidebarPanel(
# customize display settings
tags$head(
tags$style(type='text/css'
, "table.data { width: 300px; }"
, ".well {width: 80%; background-color: NULL; border: 0px solid rgb(255, 255, 255); box-shadow: 0px 0px 0px rgb(255, 255, 255) inset;}"
, ".tableinput .hide {display: table-header-group; color: black; align-items: center; text-align: center; align-self: center;}"
, ".tableinput-container {width: 100%; text-align: center;}"
, ".tableinput-buttons {margin: 10px;}"
, ".data {background-color: rgb(255,255,255);}"
, ".table th, .table td {text-align: center;}"
)
)
,
wellPanel(
h4("Input Table")
,
matrixInput(inputId = 'data', label = 'Add/Remove Rows', data = df)
,
helpText("This table accepts user input into each cell. The number of rows may be controlled by pressing the +/- buttons.")
)
)
,
mainPanel(
wellPanel(
wellPanel(
h4("Output Table")
,
tableOutput(outputId = 'table.output')
,
helpText("This table displays the input matrix together with the product of the rows of the input matrix")
)
)
)
)
)
runApp(list(ui = ui, server = server))
The value should be entered by user from shiny widgets such as selectizeInput, checkboxInput or textInput and passed to the dataframe once the user click my actionButton. What I want is pretty similar to the combination of the above functions but I don't know how to do. Any suggestions?
Many thanks in advance.
Though I ended up using none of the two packages, this worked fine:
library(shiny)
server = shinyServer(function(input, output, session){
values <- reactiveValues()
values$DT <- data.frame(Name = NA,
status = NA,
compare = NA,
stringsAsFactors = FALSE)
newEntry <- observeEvent(input$addrow, {
newLine <- c(input$textIn, input$boxIn, input$selectIn)
values$DT <- rbind(values$DT, newLine)
})
newEntry <- observeEvent(input$revrow, {
deleteLine <- values$DT[-nrow(values$DT), ]
values$DT <- deleteLine
})
output$table <- renderTable({
values$DT
})
})
ui = shinyUI(navbarPage(
"Backtest System", inverse = TRUE, id = "navbar",
tabPanel("Strategy",
sidebarLayout(
sidebarPanel(
h4("Indicator"),
textInput("textIn", "Text", "try"),
checkboxInput("boxIn", "Box", TRUE),
selectizeInput("selectIn", "Select",
choices = c(">" = ">",
">=" = ">=",
"<" = "<",
"<=" = "<=")),
actionButton("addrow", "Add Row"),
actionButton("revrow", "Remove Row")
),
mainPanel(
tableOutput("table")
)
)
)
)
)
runApp(list(ui = ui, server = server))