R Shiny: Right Align and Left Align in the Same Dropdown Menu - r

I am making an R Shiny app and would like to left align and right align in the same dropdown menu.
So in the example app:
library(shiny)
# Define UI
ui <- fluidPage(
# App title ----
titlePanel("Dropdown Problems"),
# Sidebar layout with input and output definitions
sidebarLayout(
# Sidebar panel for inputs
sidebarPanel(
# Define Dropdown Menu
selectizeInput("selection_dropdown", "Select Selection of Interest:",
choices=NULL,
options=list(
maxItems=1,
placeholder='Select Selection',
create=TRUE)
)
),
# Main panel for displaying outputs ----
mainPanel(
# Output:
plotOutput(outputId = "sample_plot")
)
)
)
server <- function(session,input, output) {
# Define New Data Frame
new_data_frame <- data.frame(column1=c("aaaaaaaa","bb","cccc"),column2=c(1,2,3),column3=c("plot_a","plot_b","plot_c"))
# Create Dropdown Menu
observe({
dropdown_choices <- paste(new_data_frame$column1," (",new_data_frame$column2,")",sep="")
updateSelectizeInput(
session,
"selection_dropdown",
choices=dropdown_choices,
server=TRUE,
)
})
# Create Output Plot (This doesn't really matter)
output$sample_plot <- renderPlot({
plot_selection <- gsub(" .*","",input$selection_dropdown)
plot_selection <- new_data_frame$column3[new_data_frame$column1==plot_selection]
plot(
x=NA,
y=NA,
xlim=c(0,100),
ylim=c(0,100)
)
text(x=50,y=50,plot_selection)
})
}
shinyApp(ui = ui, server = server)
In the dropdown menu I would like the letters to be left aligned within the dropdown and the numbers and brackets to be right aligned.
I can separate them by a tab but the numbers won't be in line with each other unfortunately.
Thanks in advance for your help.

How about this
We can use the counter trick from CSS so these numbers are automatically assigned based on the order they are displayed in the dropdown. It means you don't need to manually add the index. When it is selected, on the server, it returns the value without the index.
library(shiny)
# Define UI
ui <- fluidPage(
tags$style(
'
:root {counter-reset: mycounter;}
.selectize-dropdown-content .option::after {
counter-increment: mycounter;
content: "(" counter(mycounter) ")";
float: right;
}
'
),
# App title ----
titlePanel("Dropdown Problems"),
# Sidebar layout with input and output definitions
sidebarLayout(
# Sidebar panel for inputs
sidebarPanel(
# Define Dropdown Menu
selectizeInput("selection_dropdown", "Select Selection of Interest:",
choices=NULL,
options=list(
maxItems=1,
placeholder='Select Selection',
create=TRUE)
)
),
# Main panel for displaying outputs ----
mainPanel(
# Output:
plotOutput(outputId = "sample_plot")
)
)
)
server <- function(session,input, output) {
# Define New Data Frame
new_data_frame <- c("aaaaaaaa","bb","cccc")
# Create Dropdown Menu
observe({
updateSelectizeInput(
session,
"selection_dropdown",
choices=new_data_frame,
server=TRUE,
)
})
# Create Output Plot (This doesn't really matter)
output$sample_plot <- renderPlot({
plot_selection <- gsub(" .*","",input$selection_dropdown)
plot(
x=NA,
y=NA,
xlim=c(0,100),
ylim=c(0,100)
)
text(x=50,y=50,plot_selection)
})
}
shinyApp(ui = ui, server = server)
Updates:
If your indices are not ordered numbers, we can still do it.
I just assume your data is still sending options from the server, even though your demo data seems that it can be done purely from the UI. Imagine your indices are some random numbers. We can send these numbers as CSS style to UI and format the dropdown.
library(shiny)
library(glue)
library(magrittr)
# Define UI
ui <- fluidPage(
# App title ----
titlePanel("Dropdown Problems"),
# Sidebar layout with input and output definitions
sidebarLayout(
# Sidebar panel for inputs
sidebarPanel(
# Define Dropdown Menu
uiOutput("style"),
selectizeInput("selection_dropdown", "Select Selection of Interest:",
choices=NULL,
options=list(
maxItems=1,
placeholder='Select Selection',
create=TRUE)
)
),
# Main panel for displaying outputs ----
mainPanel(
# Output:
plotOutput(outputId = "sample_plot")
)
)
)
server <- function(session,input, output) {
# Define New Data Frame
new_data_frame <- c("aaaaaaaa","bb","cccc")
indices <- sample(999, 3)
output$style <- renderUI(
tags$style(glue(.open = '#{', .close = "}#",
'
.selectize-dropdown-content .option:nth-child(#{seq_along(indices)}#)::after {
content: "(#{indices}#)";
float: right;
}
'
) %>% glue_collapse("\n"))
)
# Create Dropdown Menu
observe({
updateSelectizeInput(
session,
"selection_dropdown",
choices=new_data_frame,
server=TRUE,
)
})
# Create Output Plot (This doesn't really matter)
output$sample_plot <- renderPlot({
plot_selection <- gsub(" .*","",input$selection_dropdown)
plot(
x=NA,
y=NA,
xlim=c(0,100),
ylim=c(0,100)
)
text(x=50,y=50,plot_selection)
})
}
shinyApp(ui = ui, server = server)

I've created a new column that combines column 1 and 2, then a little bit of Javascript is used to create HTML for each option.
It left aligns the value from column 1 and right aligns the value from column 2.
It can probably be done without creating the new column by passing the 2 columns to the Javascript function.
library(shiny)
# Define UI
ui <- fluidPage(
# App title ----
titlePanel("Dropdown Problems"),
# Sidebar layout with input and output definitions
sidebarLayout(
# Sidebar panel for inputs
sidebarPanel(
# Define Dropdown Menu
selectizeInput("selection_dropdown", "Select Selection of Interest:",
choices=NULL,
options=list(
maxItems=1,
placeholder='Select Selection',
create=TRUE)
)
),
# Main panel for displaying outputs ----
mainPanel(
# Output:
plotOutput(outputId = "sample_plot")
)
)
)
server <- function(session,input, output) {
# Define New Data Frame
new_data_frame <- data.frame(column1=c("aaaaaaaa","bb","cccc"),column2=c(1,2,3),column3=c("plot_a","plot_b","plot_c"))
new_data_frame$column4 <-paste0(new_data_frame$column1, " (", new_data_frame$column2, ")")
# Create Dropdown Menu
observe({
dropdown_choices <- new_data_frame$column4
updateSelectizeInput(
session,
"selection_dropdown",
choices=dropdown_choices,
options = list(render = I(
'{
option: function(item, escape) {
const x = item.value.split(" ");
return `<p style=\"text-align:left;\">
${x[0]}
<span style=\"float:right;\">
${x[1]}
</span>
</p>`
}
}')),
server=TRUE,
)
})
# Create Output Plot (This doesn't really matter)
output$sample_plot <- renderPlot({
plot_selection <- gsub(" .*","",input$selection_dropdown)
plot_selection <- new_data_frame$column3[new_data_frame$column1==plot_selection]
plot(
x=NA,
y=NA,
xlim=c(0,100),
ylim=c(0,100)
)
text(x=50,y=50,plot_selection)
})
}
shinyApp(ui = ui, server = server)

Related

Loading RDS data from drop down menu not changing when selection is made in shiny R

I have created a drop-down menu where each item has an rds file that can be loaded for analysis. The code works perfectly fine when only one tab exists in the interface. When I add more than one tab in the app, Only the first item in the drop-down menu is selected.
Is there an optimal way to select and upload different objects from the drop-down menu, where a selection once made would be applied to all the tabs in the app.
Here is the sample code I have.
server.R
## Load required libraries
library('dplyr')
library('data.table')
library("DT")
library("ggplot2")
library("stringi")
library("cowplot")
library("tidyverse")
shinyServer(function(input, output, session)
{
################################################################################
inputfunc <- reactive(
{
infile <- input$rdsfile
if (is.null(infile)){
return(NULL)
}
rds_file <- readRDS(paste0('path to folder data/',infile))
}
)
## Load data- UMAP plot
sum_input <- reactive(
{
rds_file <- inputfunc()
if (is.null(rds_file))
{
return(invisible())
}
else
{
sc_file <- rds_file
## Do Some plotting
}
})
## Expression plots-1
sum2_input <- reactive(
{
rds_file <- inputfunc()
if (is.null(rds_file))
{
return(invisible())
}
else
{
sc_file <- rds_file
sometasktodo <- input$sometask
## Do Some plotting
}
}
)
## Render plots
output$plot_sum.output <- renderPlot(
{
print(sum_input())
})
## Downloading plots
output$plot_sum2.output <- renderPlot(
{
print(sum2_input())
})
})
ui.R
library("shiny")
library("shinythemes")
library("ggplot2")
shinyUI(fluidPage(
# theme = "bootstrap.css",
theme = shinytheme("readable"),
# themeSelector(),
titlePanel(h3("Demo Application", style= "font-family: 'American Typewriter'; color:#081d58"), windowTitle = "scVisualizer"),
hr(),
navbarPage("",
tabPanel(h4("Load Data"),
#########################################################################
selectInput(inputId = 'rdsfile',
label = 'Choose a file:',
choices = list.files(path = "./data",
full.names = FALSE,
recursive = FALSE), selected = list.files("./data")[1]),
column(12, align="center", br(), plotOutput(outputId= 'plot_sum.output', width = "700px", height = "500px"), #50%
)),
tabPanel(h4("Some Plots"),
sidebarPanel(textInput("sometask",label="Name"),submitButton("submit"), helpText("Enter symbol")),
column(12, align="center", br(), plotOutput('plot_sum2.output', width = "1000px", height = "500px")
))
)))
Nothing happens when I select the second item in the drop-down menu.
Edit- I also tried moving the selectInput into sidebarLayout and sidebarPanel and moved all the tabs into tabsetPanel under mainPanel still it's not working.
Any suggestions would be greatly helpful!

shiny module with observeEvent updates based on previous inputs

I have an app which creates boxes. Each box has a button that triggers a modal. The modal has inputs which the user changes and then a button which triggers an action based on those inputs (basically just uploading to a database). Because each box has a different specification, I wrote a module and then loop thru a list, creating a box for each element. This works fine.
However, the flow in the modal and observeEvent has a flaw: the first run thru I get the desired results, but on the second occasion in the same box (same id module), after pressing the modal button to update, it will not use the new inputs, but rather what happened in the first run. I am guessing it has something to do with the namespace/observeEvent combination as I might be triggering the event with a "stored" namespace? Would I need to somehow "flush" the namespace after every update? Anyway, any help appreciated as it gets confusing fast with all the namespace/modules combinations.
library(shiny)
library(shinyWidgets)
ui <- navbarPage(
'page', collapsible = TRUE,
tabPanel("test",
useSweetAlert(),
sidebarLayout(
sidebarPanel(),
mainPanel(
uiOutput('all_products_ui')
)
)
)) # end navbar
server <- shinyServer(function(input, output) {
list_products <- c(1,2,3,4,5)
# Now, I will create a UI for all the products
output$all_products_ui <- renderUI({
r <- tagList()
progress_move <- 0
for(k in 1:length( list_products )){
r[[k]] <- ExistingProductUI(id = k, product = list_products[[k]] )
}
r
})
# handlers duplicate a call to module depending on the id of ExistingProductUI
handlers <- list()
observe(
handlers <<- lapply(seq.int(length( list_products )),
function(i) {
callModule(ExistingProductUpdate,
id = i,
product = list_products[[i]] )
})
)
handlers
}) # end of server ----
# UI module ------------------------------------------------------
ExistingProductUI <- function(id, product){
ns <- NS(id)
box(title = as.character(p$title),
product["title"],
footer = tagList(
actionBttn(
inputId = ns("change_selected"), label = "change"),
)
)
}
# server module ------------------------------------------------------
ExistingProductUpdate <- function(input, output, session, product){
ns <- session$ns
observeEvent(input$change_selected, {
# when box button is clicked for this product (id)
# FIRST: show a modal
showModal(
modalDialog(
title = "what do you want to change?",
tagList(
radioGroupButtons(inputId = ns("change_selected_choice"), labels = "change x", choices = c(1,2,3,4)),
sliderInput(ns("change_selected_pct"), "change y:", min = -50, max = 100, value = 0, step = 5)
),
easyClose = TRUE,
footer = tagList(
actionButton(ns("change_selected_submit"), "submit!", icon = icon("check")),
modalButton("never mind")
)
)
)
# SECOND: when change_selected_submit is clicked,
observeEvent(input$change_selected_submit, {
# do some calculations with product using what I inputed in modal ---
# then, update a table ----
functionToUploadThings(product, input$change_selected_choice)
# THIRD: Close with a confirmation
sendSweetAlert(
session,
title = "Success!",
type = "success",
btn_labels = "Ok",
closeOnClickOutside = TRUE,
width = NULL
)
})
})
}
Below is a solution that works. The problem was that you nested your observeEvent in the module. I'm not entirely sure why this led to problems, some values weren't processed correctly. However, you don't need to nest the observeEvent, the second one gets also triggered by the actionButton in the modal when it is by its own. Additionally, I included a removeModal before the success notification is shown:
library(shiny)
library(shinyWidgets)
library(shinydashboard)
ui <- navbarPage(
'page', collapsible = TRUE,
tabPanel("test",
useSweetAlert(),
sidebarLayout(
sidebarPanel(),
mainPanel(
uiOutput('all_products_ui')
)
)
)) # end navbar
server <- shinyServer(function(input, output) {
list_products <- c(1,2,3,4,5)
# Now, I will create a UI for all the products
output$all_products_ui <- renderUI({
r <- tagList()
progress_move <- 0
for(k in 1:length( list_products )){
r[[k]] <- ExistingProductUI(id = k, product = list_products[[k]] )
}
r
})
# handlers duplicate a call to module depending on the id of ExistingProductUI
handlers <- list()
observe(
handlers <<- lapply(seq.int(length( list_products )),
function(i) {
callModule(ExistingProductUpdate,
id = i,
product = list_products[[i]] )
})
)
handlers
}) # end of server ----
# UI module ------------------------------------------------------
ExistingProductUI <- function(id, product){
ns <- NS(id)
box(title = as.character(product),
product,
footer = tagList(
actionBttn(
inputId = ns("change_selected"), label = "change"),
)
)
}
# server module ------------------------------------------------------
ExistingProductUpdate <- function(input, output, session, product){
ns <- session$ns
observeEvent(input$change_selected, {
# when box button is clicked for this product (id)
# FIRST: show a modal
showModal(
modalDialog(
title = "what do you want to change?",
tagList(
radioGroupButtons(inputId = ns("change_selected_choice"), label = "change x", choices = c(1,2,3,4)),
sliderInput(ns("change_selected_pct"), "change y:", min = -50, max = 100, value = 0, step = 5)
),
easyClose = TRUE,
footer = tagList(
actionButton(ns("change_selected_submit"), "submit!", icon = icon("check")),
modalButton("never mind")
)
)
)
})
# SECOND: when change_selected_submit is clicked,
observeEvent(input$change_selected_submit, {
# do some calculations with product using what I inputed in modal ---
# then, update a table ----
# functionToUploadThings(product, input$change_selected_choice)
# THIRD: Close with a confirmation
removeModal()
sendSweetAlert(
session,
title = "Success!",
type = "success",
btn_labels = "Ok",
closeOnClickOutside = TRUE,
width = NULL
)
})
}
shinyApp(ui, server)
Please note: I made some modifications to make your MWE work:
include library(shinydashboard)
p$title and product["title"] to product
change labels to label in radioGroupButtons
comment out functionToUploadThings(product, input$change_selected_choice)
Edit
I'm still not super sure what happens when nesting the observeEvents. I made a small toy example and played around with the reactlog. It seems that nesting the observers generates a new observer for button2 every time button1 is clicked. These observers are not removed and lead to unwanted behaviour. In contrast, when using separate observeEvents, the observer for button2 is only created once.
library(shiny)
library(reactlog)
ui <- fluidPage(
actionButton("button1", "click")
)
server <- function(input, output, session) {
observeEvent(input$button1, {
print("from first observer")
print(input$button2)
showModal(
modalDialog(
title = "what do you want to change?",
"some text",
easyClose = TRUE,
footer = tagList(
actionButton("button2", "submit!", icon = icon("check")),
modalButton("never mind")
)
)
)
# nested observer -> leads to remaining observers
observeEvent(input$button2, {
print("from second observer")
print(input$button2)
removeModal()
})
})
# independent observer -> generates only one observer
# observeEvent(input$button2, {
# print("from second observer")
# print(input$button2)
# removeModal()
# })
}
shinyApp(ui, server)

How to display reactive UI for afvanced settings in modalDialog with Shinyusing moduals

I'm designing an app that will have multiple tabs that all have basic ui features on the side, but hid various advanced UI options in a pop-up modal button. Some of these options are responsive to the dataset, for example choosing which rows to use.
I want to use modules to design the app, but am running into trouble getting reactive UI elements into the modals.
I think modalUI1 is getting the right namespace, but when I call uiOutput on the reactive ui element made by settingsServer1 it doesn't show up.
The closest slack post I found did something similar, but the modal they had static UI.Here
# Basic View Functions ----------------------------------------------------
# These modules setup the basic view for each analysis
simpleui <- function(id){
ns <- NS(id)
selectData <- selectInput(ns("d"), "Dataset:",
c("iris",
"mtcars",
"iris3"))
outputRows <- verbatimTextOutput(ns("df"))
settingsButton<-
actionButton(
ns("settings"),
"Settings")
tabPanel(id,
tagList(
p(),
selectData,
p(),
outputRows,
p(),
settingsButton
)
)
}
view1 <- function(input, output, session) {
reactiveDf <- reactive({
switch(input$d,
"iris" = iris,
"mtcars" = mtcars,
"iris3"= iris3)
})
output$df <- renderText({nrow(reactiveDf())})
callModule(settingsServer1, "settings", reactiveDf)
observeEvent(input$settings, {
showModal(settngsModal(session$ns))
})
settngsModal <- function(ns) {
# ns <- NS(id) ### This is inner UI so passed namespace from outer
modalDialog(
modalUI1("settings"), ### Call innerModalUI.
# withTags({ # UI elements for the modal go in here
# fluidRow(
# column(4, "Inputs","Sectionnormal",uiOutput("nrowSlide")),
# column(4, "Inputs","Sectionnormal",uiOutput(ns("nrowSlide")))
# )}
,
title = "Settings",
footer = modalButton("Dismiss"),
size = "l",
easyClose = FALSE,
fade = TRUE)
}
}
# Advanced settings hidden in modal ---------------------------------------
# These functions should hide the advanced UI settings in a Modal.
modalUI1 <- function(ns) {
### Several UI elements 1 of which chooses which first N Rows.
### The slider is reactive
# reactiveSlider <- uiOutput("nrowSlide")
withTags({ # UI elements for the modal go in here
fluidRow(
# print(ns("nrowSlide")),
# print(input),
column(4, "---------",uiOutput(("nrowSlide")), "---------")
)
})
}
settingsServer1 <- function(input, output, session, reactiveDf){
output$nrowSlide <- renderUI({
sliderInput("obs", "Number of observations:",
min = 1, max = nrow(reactiveDf()), value = 1)
})
}
# Basic Setup -------------------------------------------------------------
ui <- shinyUI(navbarPage("My Application",
simpleui("v1"),
simpleui("v2")
))
server <- function(input, output, session) {
callModule(view1, "v1")
callModule(view1, "v2")
### Also look for event to create a modal.
### This modal will have reactive items.
}
shinyApp(ui, server)

Switch outputs in R shiny radio buttons

I am working on a shiny app where I am using radioGroupButtons from ShinyWidgets. So for each button I am trying to switch to different output like table or a plot. How do I link the radio button to the outputs
library(shinyWidgets)
library(shinipsum)
library(htmlwidgets)
ui <- navbarPage(
div(
id = "section1-1",
radioGroupButtons(
inputId = "Id069",
# label = "Choose a graph :",
choices = c(
`<i class='fa fa-bar-chart'></i>` = "bar",
`<i class='fa fa-line-chart'></i>` = "line",
`<i class='fa fa-pie-chart'></i>` = "pie"
),
justified = TRUE
)
)
)
server <- function(input, output, session) {
# observe({
# x <- input$inRadioButtons
#
# # Can also set the label and select items
# updateRadioButtons(session, "inRadioButtons2",
# label = paste("radioButtons label", x),
# choices = x,
# selected = x
# )
# })
output$plot <- renderPlot({
random_ggplot()
})
}
shinyApp(ui, server)
On the server side, you can access the input ID like this:
Value = input$Id069
So, add logic like this to server side (within output$plot):
If (Value == x) {
Plot1()
} else {
Plot2()
}
You might want to look into conditional panels on the UI.

How can i make sidebar panel dynamic in shiny?

**#Ui.code:**
library(shiny)
fluidPage(
titlePanel(title=h2(" Lucas Tvs",align="center")),
sidebarPanel(
conditionalPanel(condition="input.tabs1=='Profit Loss'",
selectInput("Operations","Select the desired Profit Loss statement",choices = profitloss1$Operations)),
br(),
conditionalPanel(condition="input.tabs1=='Profit Loss'",
selectInput("summary1","Select the desired Summary",choices = summary1$OPERATIONS)),
conditionalPanel(condition="input.tabs1=='Ratio'",
selectInput("Ratio","Select the desired Ratios",choices=ratios1$Ratios)),
br(),
conditionalPanel(condition="input.tabs1=='Ratio'",
selectInput("summary","Select the desired Summary",choices = summary$RATIO)),
conditionalPanel(condition="input.tabs1=='Balancesheet'",
selectInput("Particulars","Select the desired Balancesheet statement", choices = Balancesheet$Particulars)),
br(),
conditionalPanel(condition="input.tabs1=='Balancesheet'",
selectInput("summary2","Select the desired Summary",choices = summary2$PARTICULARS))
),
mainPanel(
tabsetPanel(id="tabs1",
tabPanel("Profit Loss",column(5,tableOutput("profitloss")), column(7,plotOutput("plot")),tableOutput("summary1")),
tabPanel("Ratio",column(5,tableOutput("Ratio")),column(7,plotOutput("plot2")),tableOutput("summary")),
tabPanel("Balancesheet",column(5,tableOutput("Balancesheet")),column(6,plotOutput("plot1")),tableOutput("summary2"))
)
)
)
In the UI part I have created 3 tabs in the main panel and 2 select inputs in the sidebar panel.All those are reacting dynamically but now I want my code to show me that both the select input in the side bar panel are mutually dependent on each other.
#server code:
library(shiny)
library(ggplot2)
shinyServer(function(input,output){
output$profitloss<-renderTable({
oporationfilter<-profitloss1[profitloss1$Operations==input$Operations,c("Years","Value")]
})
output$Ratio<-renderTable({
ratiofilter<-ratios1[ratios1$Ratios==input$Ratio,c("Years","Value")]
})
output$Balancesheet<-renderTable({
Balancesheetfilter<-Balancesheet[Balancesheet$Particulars==input$Particulars,c("Years","Value")]
})
output$summary<-renderTable({
summaryfilter<-summary[summary$RATIO==input$summary,c("Mean","Standard.Deviation","CAGR.1","CAGR.3","CAGR.5")]
})
output$summary1<-renderTable({
summary1filter<-summary1[summary1$OPERATIONS==input$summary1,c("Mean","Standard.Deviation","CAGR.1","CAGR.3","CAGR.5","CAGR.7")]
})
output$summary2<-renderTable({
summary2filter<-summary2[summary2$PARTICULARS==input$summary2,c("Mean","Standard.Deviation","CAGR.1","CAGR.3","CAGR.5")]
})
output$plot<-renderPlot({
options(scipen = 999)
p<-ggplot(data = profitloss1[profitloss1$Operations==input$Operations,]
,aes(x=Years,y=Value))
p+geom_line()+xlab("Years")+ylab("Value in Lakhs")+ggtitle("Profitloss Plot")
})
output$plot2<-renderPlot({
q<-ggplot(data = ratios1[ratios1$Ratios==input$Ratio,]
,aes(x=Years,y=Value))
q+geom_line()+xlab("Years")+ylab("value in lakhs")+ggtitle("Ratios Plot ")
})
output$plot1<-renderPlot({
q<-ggplot(data = Balancesheet[Balancesheet$Particulars==input$Particulars,]
,aes(x=Years,y=Value))
q+geom_line()+xlab("Years")+ylab("value in lakhs")+ggtitle("Balancesheet Plot")
})
})
This is the code for designing of the website now I want my both the select input should be mutually dependent.
for example: If select in XXX in drop down menu I should automatically get the same output in the 2nd select input.

Resources