This app is creating a vector of standardised names which I create given some user input (number of channels and replicates). An example of the standard names given the number of channels = 4 and and replicates = 1 is as follows:
c("rep1_C0","rep1_C1","rep1_C2","rep1_C3")
I would like to allow the user to replace the value of the selection with their own custom value. For example to change the input "rep1_C0" to "Control_rep1". And then for it to then update the reactive vector in question. Here is my code:
library(shiny)
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
fluidRow(column(5, numericInput("chans","# Channels",value = 4, min = 1)),
column(5, numericInput("reps","# Replicates",value = 1,min = 1))
),
uiOutput("selectnames")
),
mainPanel(
tableOutput("testcols")
)
)
))
server <- shinyServer(function(input, output) {
standardNames <- reactive({
paste("rep",rep(1:input$reps,each = input$chans),"_",
rep(paste("C",0:(input$chans - 1), sep = ""),input$reps),sep = "")
})
output$selectnames <- renderUI({
selectizeInput("selectnames", "Change Names", choices = standardNames(),
options = list(maxOptions = input$reps * input$chans))
})
## output
output$testcols <- renderTable({
standardNames()
})
})
shinyApp(ui = ui, server = server)
Is there some kind of option I can pass in the options sections that will allow this?
With selectizeInput you can set options = list(create = TRUE) to allow the user to add levels to the selection list, but I don't think that is what you want.
Instead, here is code that generates a text input box for each of the standard names, and allows the user to enter a label for them. It uses lapply and sapply to loop over each value and generate/read the inputs.
library(shiny)
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
fluidRow(column(5, numericInput("chans","# Channels",value = 4, min = 1)),
column(5, numericInput("reps","# Replicates",value = 1,min = 1))
),
uiOutput("setNames")
),
mainPanel(
tableOutput("testcols")
)
)
))
server <- shinyServer(function(input, output) {
standardNames <- reactive({
paste("rep",rep(1:input$reps,each = input$chans),"_",
rep(paste("C",0:(input$chans - 1), sep = ""),input$reps),sep = "")
})
output$setNames <- renderUI({
lapply(standardNames(), function(thisName){
textInput(paste0("stdName_", thisName)
, thisName
, thisName)
})
})
labelNames <- reactive({
sapply(standardNames()
, function(thisName){
input[[paste0("stdName_", thisName)]]
})
})
## output
output$testcols <- renderTable({
data.frame(
stdName = standardNames()
, label = labelNames()
)
})
})
shinyApp(ui = ui, server = server)
If you want to hide the list unless the user wants to add labels, you can use a simple checkbox, like this, which hides the label making list until the use checks the box to show it.
library(shiny)
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
fluidRow(column(5, numericInput("chans","# Channels",value = 4, min = 1)),
column(5, numericInput("reps","# Replicates",value = 1,min = 1))
)
, checkboxInput("customNames", "Customize names?")
, uiOutput("setNames")
),
mainPanel(
tableOutput("testcols")
)
)
))
server <- shinyServer(function(input, output) {
standardNames <- reactive({
paste("rep",rep(1:input$reps,each = input$chans),"_",
rep(paste("C",0:(input$chans - 1), sep = ""),input$reps),sep = "")
})
output$setNames <- renderUI({
if(!input$customNames){
return(NULL)
}
lapply(standardNames(), function(thisName){
textInput(paste0("stdName_", thisName)
, thisName
, thisName)
})
})
labelNames <- reactive({
if(!input$customNames){
return(standardNames())
}
sapply(standardNames()
, function(thisName){
input[[paste0("stdName_", thisName)]]
})
})
## output
output$testcols <- renderTable({
data.frame(
stdName = standardNames()
, label = labelNames()
)
})
})
shinyApp(ui = ui, server = server)
Alternatively, if you think the user may want to only change one or a small number of labels, here is a way to allow them to choose which standard name they are applying a label to:
library(shiny)
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
fluidRow(column(5, numericInput("chans","# Channels",value = 4, min = 1)),
column(5, numericInput("reps","# Replicates",value = 1,min = 1))
)
, uiOutput("setNames")
),
mainPanel(
tableOutput("testcols")
)
)
))
server <- shinyServer(function(input, output) {
vals <- reactiveValues(
labelNames = character()
)
standardNames <- reactive({
out <- paste("rep",rep(1:input$reps,each = input$chans),"_",
rep(paste("C",0:(input$chans - 1), sep = ""),input$reps),sep = "")
vals$labelNames = setNames(out, out)
return(out)
})
output$setNames <- renderUI({
list(
h4("Add labels")
, selectInput("nameToChange", "Standard name to label"
, names(vals$labelNames))
, textInput("labelToAdd", "Label to apply")
, actionButton("makeLabel", "Set label")
)
})
observeEvent(input$makeLabel, {
vals$labelNames[input$nameToChange] <- input$labelToAdd
})
## output
output$testcols <- renderTable({
data.frame(
stdName = standardNames()
, label = vals$labelNames
)
})
})
shinyApp(ui = ui, server = server)
Related
In this shiny App (code below), I need that the button labeled 'Customized' returns:
Select var X: disp
Select var Y: drat
Point size: 1.0
This necessity is a bit similar to the reset button available on the R package 'shinyjs', with the diference of that the reset button returns to the code's default.
library(shiny)
library(shinyjs)
shinyApp(
ui <- fluidPage(
sidebarPanel(
fluidRow(
shinyjs::useShinyjs(),
id = "panel_vars",
# Buttons
uiOutput("varx"),
uiOutput("vary"),
numericInput("ptSize", "Point size",
min=.1, max=5, value = 2),
actionButton("p1", 'Reset variables XY'),
helpText(""),
actionButton("p2", 'Customized')
)
),
mainPanel(
plotOutput("plot")
)
),
server <- function(input, output) {
getModel <- reactive({
names(mtcars)
})
output$varx <- renderUI({
selectInput("varsel.x", "Select var X",
choices = as.list(getModel()), multiple = F)
})
output$vary <- renderUI({
selectInput("varsel.y", "Select var Y",
choices = as.list(getModel()), multiple = F)
})
observeEvent(input$p1, {
shinyjs::reset("panel_vars")
})
output$plot <- renderPlot({
p <- mtcars %>% ggplot(aes_string(x=input$varsel.x, y=input$varsel.y))+
geom_point(size=input$ptSize)
p
})
}
)
You can simply use updateSelectInput and updateNumericInput to do so:
library(shiny)
library(shinyjs)
library(dplyr)
library(ggplot2)
shinyApp(
ui <- fluidPage(
sidebarPanel(
fluidRow(
shinyjs::useShinyjs(),
id = "panel_vars",
# Buttons
uiOutput("varx"),
uiOutput("vary"),
numericInput("ptSize", "Point size",
min=.1, max=5, value = 2),
actionButton("p1", 'Reset variables XY'),
helpText(""),
actionButton("p2", 'Customized')
)
),
mainPanel(
plotOutput("plot")
)
),
server <- function(input, output,session) {
getModel <- reactive({
names(mtcars)
})
output$varx <- renderUI({
selectInput("varsel.x", "Select var X",
choices = as.list(getModel()), multiple = F)
})
output$vary <- renderUI({
selectInput("varsel.y", "Select var Y",
choices = as.list(getModel()), multiple = F)
})
observeEvent(input$p1, {
shinyjs::reset("panel_vars")
})
output$plot <- renderPlot({
req(input$varsel.x,input$varsel.y,input$ptSize)
p <- mtcars %>% ggplot(aes_string(x=input$varsel.x, y=input$varsel.y))+
geom_point(size=input$ptSize)
p
})
observeEvent(input$p2, {
updateSelectInput(session,'varsel.x',selected = 'disp')
updateSelectInput(session,'varsel.y',selected = 'drat')
updateNumericInput(session, "ptSize", value = 1.0)
})
}
)
I want the user of my Shiny app to be able to choose between two types of plots by clicking on radiobuttons in the Events panel. The code I have written works, but the page leaves a huge white space when going from "Map" to "Plot". Is there any way to get rid of the white space and position the plot at the very top?
# Load R packages
library(shiny)
library(shinythemes)
library(tidyverse)
library(leaflet)
set.seed(123)
year <- 2001:2020
event <- sample(1:100, size = 20, replace = TRUE)
dat <- as.data.frame(cbind(year, event))
# Define UI
ui <- fluidPage(
shinyjs::useShinyjs(),
theme = shinytheme("journal"),
navbarPage(
"Title",
tabPanel("About",
),
tabPanel("Events",
fluidPage(
titlePanel("Title"),
sliderInput("range", label = "Move slider to select time period", min(2001), max(2020),
value = range(2001:2002), step = 1, sep = "", width = "65%"),
sidebarLayout(
sidebarPanel(
radioButtons("plotType", "Plot type", choices = c("Map" = "m", "Chart" = "l"))),
mainPanel(
leafletOutput("map"),
plotOutput("plot"))
)
)
)
)
)
# Define server function
server <- function(input, output, session) {
observeEvent(input$plotType, {
if(input$plotType == "l"){
shinyjs::disable("range")
}else{
shinyjs::enable("range")
}
})
output$plot <- renderPlot({
if (input$plotType == "l") {
ggplot(dat, aes(year, event)) +
geom_line() +
labs(x = "Year", y = "Events") +
theme_bw()
}
})
output$map <- renderLeaflet({
if ( input$plotType == "m") {
leaflet(dat) %>% addTiles() %>%
fitBounds(~min(11), ~min(54), ~max(67), ~max(24))
}
})
} # server
# Create Shiny object
shinyApp(ui = ui, server = server)
There is a big space because the map html object still exists, but is empty. To avoid this, I created and observeEvent that hides or show the map output depending on input value. I did the same thing with the plot, in cas you need to add others elements below it.
Please note that there are others solutions (conditionalPanel for example), I am just giving you the one I think is the simpliest here.
# Load R packages
library(shiny)
library(shinythemes)
library(tidyverse)
library(leaflet)
set.seed(123)
year <- 2001:2020
event <- sample(1:100, size = 20, replace = TRUE)
dat <- as.data.frame(cbind(year, event))
# Define UI
ui <- fluidPage(
shinyjs::useShinyjs(),
theme = shinytheme("journal"),
navbarPage(
"Title",
tabPanel("About",
),
tabPanel("Events",
fluidPage(
titlePanel("Title"),
sliderInput("range", label = "Move slider to select time period", min(2001), max(2020),
value = range(2001:2002), step = 1, sep = "", width = "65%"),
sidebarLayout(
sidebarPanel(
radioButtons("plotType", "Plot type", choices = c("Map" = "m", "Chart" = "l"))),
mainPanel(
leafletOutput("map"),
plotOutput("plot"))
)
)
)
)
)
# Define server function
server <- function(input, output, session) {
# hide or show map and plot
observeEvent(input$plotType, {
if(input$plotType == "l"){
shinyjs::disable("range")
shinyjs::hide("map")
shinyjs::show("plot")
}
if(input$plotType == "m"){
shinyjs::enable("range")
shinyjs::show("map")
shinyjs::hide("plot")
}
})
output$plot <- renderPlot({
req(input$plotType == "l") # good practice to use req instead of if
ggplot(dat, aes(year, event)) +
geom_line() +
labs(x = "Year", y = "Events") +
theme_bw()
})
output$map <- renderLeaflet({
req(input$plotType == "m")
leaflet(dat) %>% addTiles() %>%
fitBounds(~min(11), ~min(54), ~max(67), ~max(24))
})
} # server
# Create Shiny object
shinyApp(ui = ui, server = server)
I have tried this in different ways and achieved one task, either add or delete., but i couldn't get complete solution in one, i might be missing some small concept somewhere.. I am adding the code , please help me complete my basic app.
library(shiny)
library(DT)
x<- data.frame(v1 = NA,
v2 = NA
),
ui = shinyUI(
fluidPage(
sidebarLayout(
sidebarPanel(
textInput("v1","v1","a"),
numericInput("V2","V2","1"),
# Row selection
numericInput(inputId = "row.selection", label = "Select row to be
deleted", min = 1, max = 100, value = "")
# Add button
actionButton(inputId = "add.button", label = "Add", icon =
icon("plus")),
# Delete button
actionButton(inputId = "delete.button", label = "Delete", icon =
icon("minus")),
),
mainPanel(
dataTableOutput('table')
)
)
)
),
Server side code
server = function(input, output, session) {
values <- reactiveValues()
values$df <- x
newEntry <- observe({
cat("newEntry\n")
if(input$add.button > 0) {
newRow <- data.frame(input$v1, input$v2)
isolate(values$df <- rbind(values$df,newRow))
}
})
deleteEntry <- observe({
cat("deleteEntry\n")
if(input$delete.button > 0) {
if(is.na(isolate(input$row.selection))){
values$df <- isolate(values$df[-nrow(values$df), ])
} else {
values$df <- isolate(values$df[-input$row.selection, ])
}
}
})
output$table = renderDataTable({
values$df
})
}
Try to use observeEvent rather than obser with actionbutton
and also, you have uppercase and lowercase issue with input$v2 (should be input$V2)
Try this modified code:
library(shiny)
library(DT)
x<- data.frame(v1 = NA,
v2 = NA
)
ui = shinyUI(
fluidPage(
sidebarLayout(
sidebarPanel(
textInput("v1","v1","a"),
numericInput("V2","V2","1"),
# Row selection
numericInput(inputId = "row.selection", label = "Select row to be
deleted", min = 1, max = 100, value = ""),
# Add button
actionButton(inputId = "add.button", label = "Add", icon =
icon("plus")),
# Delete button
actionButton(inputId = "delete.button", label = "Delete", icon =
icon("minus"))
),
mainPanel(
dataTableOutput('table')
)
)
)
)
server = function(input, output, session) {
values <- reactiveValues()
values$df <- x
observeEvent(input$add.button,{
cat("addEntry\n")
print(input$v1)
print(input$V2)
newRow <- data.frame(input$v1, input$V2)
colnames(newRow)<-colnames(values$df)
values$df <- rbind(values$df,newRow)
print(nrow(values$df))
})
observeEvent(input$delete.button,{
cat("deleteEntry\n")
if(is.na(input$row.selection)){
values$df <- values$df[-nrow(values$df), ]
} else {
values$df <- values$df[-input$row.selection, ]
}
})
output$table = renderDataTable({
values$df
})
}
shinyApp(ui,server)
Just run all the code above, and it should work properly.
I have a list object that I would like to update with a reactive input. The object has to be a list.
Below is my minimal code. Thanks in advance.
library(shiny)
shinyApp(
ui = fluidPage(
fluidRow(
numericInput("number", "Change number", 10, min = 1, max = 100),
verbatimTextOutput('show')
)
),
server = function(input, output, session) {
QG <- list(a = c(1:10),
b = LETTERS[1:10])
#How to update reactiveVal from reactive input
#QG$a[2] <- input$number
output$show <- renderPrint({
QG$a[2]
})
}
)
Something like this?
library(shiny)
QG <- list(a = c(1:10),b = LETTERS[1:10])
shinyApp(
ui = fluidPage(
fluidRow(
numericInput("number", "Change number", 10, min = 1, max = 100),
verbatimTextOutput('show')
)
),
server = function(input, output, session) {
Values <- reactive({
QG$a[2] <- as.numeric(input$number)
QG
})
output$show <- renderPrint({
Values()
})
}
)
EDITE :
I added one more example that is same as my problem. I want to update the checkboxGroupInput as dataset changes. And also want to render the table with checked column in the checkboxGroupInput options.
SERVER
shinyServer( function(input, output, session) {
dataset_list <- list( "rock" = rock,
"pressure" = pressure,
"cars" = cars
)
observeEvent( input$n_select_input, {
selected_dataset <- reactive({
selected_list <- list()
for( i in 1:input$n_select_input ){
selected_list[[i]] <- dataset_list[[i]]
}
names(selected_list) <- names( dataset_list )[1:input$n_select_input]
selected_list
})
colname_indata_list <- reactive({
colname.indata.list <- list()
for( set in names( selected_dataset() ) ){
colname.indata.list[[set]] <- colnames( selected_dataset()[[set]] )
}
colname.indata.list
})
choice_cand <- reactive({ names(selected_dataset()) })
updateSelectInput( session,
"dataset",
choices = as.character( choice_cand() )
)
choices_cand <- reactive({ colname_indata_list()[[input$dataset]] })
updateCheckboxGroupInput( session,
"column",
choices = as.character( choices_cand() )
)
})
# observeEvent( input$dataset, {
#
# choices_cand <- reactive({ colname_indata_list()[[input$dataset]] })
# updateCheckboxGroupInput( session, "column",
# choices = as.character( choices_cand() ) )
#
# })
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
output$table <- renderTable({
datasetInput()
})
} )
I tried the code above in the ## , it didn't work.
UI
shinyUI(
fluidPage( titlePanel('Downloading Data'),
sidebarLayout(
sidebarPanel( numericInput( "n_select_input", "n select inpur", 1,
min=1, max=3 ),
selectInput("dataset", "Choose a dataset:",
choices = "" ),
checkboxGroupInput( "column", "select column",
choices = "")
),
mainPanel(
tableOutput('table')
)
)
)
)
FIRST WRITE :
I simplified the code below. I want to use updated select input as a output for the updatedCheckboxGroupInput 's choices. But the updated select input, input$select_group is null. I tried varies solutions.. but couldn't solve it.
If you just run this code right away, it doesn't work..
fund_group <- reactive({ # this is the list of fund group including fund name
# for example,
"domestic" = c("a","b", "c"),
"global" = c( "aa", "bb", "cc")
# list name and fund name in the list are changable
})
I want to update selectInput choices as names(fund_group) changes.
So I used code below,
observe({
group_name <- reactive({ names(fund_group()) })
updateSelectInput( session,
"select_group",
choices = group_name() )
fund_list <- reactive({ fund_group()[[input$select_group]] })
updateCheckboxGroupInput( session,
"fund_in_group",
choices = fund_list(),
selected = fund_list() )
})
For the UI,
narvarPage( "narvarTitle",
tabPanel( "tab panel",
fluidRow( column( 3, wellPanel( textOutput( "fixed_anal_date" ),
br(),
br(),
selectInput( "select_group",
label = "Select group",
choices = "" ),
br(),
checkboxGroupInput( "fund_in_group",
label = "Select funds :",
choices = "" ),
br()
) )
) )
Thank you for reading the messy code...
I have edited the code an its working now. You should put the two input in two different observe.
Here is the server. R:
server <- function(input, output, session){
fund_group <- reactive({ # this is the list of fund group including fund name
# for example,
list("domestic" = c("a","b", "c"), "global" = c( "aa", "bb", "cc"))
# list name and fund name in the list are changable
})
observe({
group_name <- reactive({ names(fund_group()) })
updateSelectInput( session,
"select_group",
choices = group_name() )
})
observe({
fund_list <- reactive({ fund_group()[[input$select_group]] })
updateCheckboxGroupInput( session,
"fund_in_group",
choices = fund_list(),
selected = fund_list())
})
}
Here is the ui.R:
ui <- navbarPage( "narvarTitle",
tabPanel( "tab panel",
fluidRow( column( 3, wellPanel( textOutput( "fixed_anual_date" ),
br(),
br(),
selectInput( "select_group",
label = "Select group",
choices = "" ),
br(),
checkboxGroupInput( "fund_in_group",
label = "Select funds :",
choices = "" ),
br()
) )
) )
)
[EDIT]:
As per your latest update I have modified the server code so that checkboxGrouptInput is updated as dataset changes and also the table is rendered for the checked columns.
shinyServer( function(input, output, session) {
dataset_list <- list( "rock" = rock,
"pressure" = pressure,
"cars" = cars
)
observeEvent( input$n_select_input, {
selected_dataset <- reactive({
selected_list <- list()
for( i in 1:input$n_select_input ){
selected_list[[i]] <- dataset_list[[i]]
}
names(selected_list) <- names( dataset_list )[1:input$n_select_input]
selected_list
})
colname_indata_list <- reactive({
colname.indata.list <- list()
for( set in names( selected_dataset() ) ){
colname.indata.list[[set]] <- colnames( selected_dataset()[[set]] )
}
colname.indata.list
})
choice_cand <- reactive({
names(selected_dataset()) })
updateSelectInput( session,
"dataset",
choices = as.character( choice_cand() )
)
######################################Start: Modified#############################
observe({
input$dataset ##Added so that this observe is called when the input$dataset changes
choices_cand <- reactive({
colname_indata_list()[[input$dataset]] })
updateCheckboxGroupInput( session,
"column",
choices = as.character( choices_cand()) ,
selected = as.character( choices_cand())
)
})
})
######################################End: Modified#############################
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
output$table <- renderTable({
datasetInput()[,input$column]##only selected columns are displayed
})
} )
Hope this helps!