I want to translate parts of my UI in a modularized shiny app. As I summarized my simplified code,
in the first module I have no problem with i18n as it is enters to the module 1 with argument i18n and
translation works well in registerUI(based on recommendation here). But my problem is with the UI of module 2 (M2UI) which this function
itself called within the server of module 1 (register) to return a modal dialogue. But i18n not detected and translation not works on displayed new modal. Any suggestions why this happens? thanks in advance...
I edited my example and it is now completely reproducible. Translation csv files are available here. Just copy them in "translations" forlder. And, the modules should be copied to "modules" folder.
## CSV translation files are available at : https://github.com/Appsilon/shiny.i18n/tree/master/examples/data
# Copy "translation_it.csv" and "translation_pl.csv" files to "translations" folder
###### make modules and copy them into folder "modules"
source("modules/register.R")
source("modules/M2.R")
#####
library(shiny)
library(shiny.i18n)
library(shinydashboard)
i18n <- Translator$new(translation_csvs_path = "translations")
i18n$set_translation_language("en")
shiny.i18n::usei18n(i18n)
############################ UI
header <- dashboardHeader(title = i18n$t('Hello Shiny!'), titleWidth = 400 ,
tags$li( fluidRow(
shiny.i18n::usei18n(i18n),
div(style="display: inline-block;vertical-align:top; font-size: 10px; height=30px;width: 150px;",selectInput(
inputId='selected_language',
label=i18n$t('Change language'),
choices = i18n$get_languages(),
selected = i18n$get_key_translation()
))
),
class = "dropdown")
)
# Sidebar Menu ------------------------------------------------------------
sidebar <- dashboardSidebar(width = 220,
sidebarMenu(
menuItem( i18n$t("Hello Shiny!"), tabName = "diary", icon = icon("align-justify")),
menuItem("Help", tabName = "help", icon = icon("table")),
#menuItem("Data analysis", tabName = "descriptive", icon = icon("chart-bar")),
menuItem("About", tabName = "about", icon = icon("info-circle"))
)
)
body <- dashboardBody(
tabItems(
tabItem("diary",
# includeMarkdown("Introduction.Rmd"),
# includeMarkdown("Contact.Rmd")
titlePanel(i18n$t("Hello Shiny!")),
sidebarLayout(
sidebarPanel(
sliderInput("bins",
i18n$t("Number of bins:"),
min = 1,
max = 50,
value = 30)
),
mainPanel(
plotOutput("distPlot"),
actionButton("test","test"),
p(i18n$t("This is description of the plot."))
)
),
tags$style(type = "text/css", ".recalculating {opacity: 1.0;}"), # Prevents gray screen during Sys.sleep()
),
tabItem("help",
),
tabItem("about",
)
)
)
ui <- dashboardPage(title = 'Coronavirus', header, sidebar, body, skin='blue')
#################################### SERVER
server <- function(input, output,session) {
observeEvent(input$selected_language, {
update_lang(session, input$selected_language)
})
shiny::observeEvent(input$test, {
registerUI(id = "REG",reg_title=i18n$t("Hello Shiny!"),i18n=i18n ) #This ID should be mached with ID in server
})
callModule(register,id = "REG", title= i18n$t("Hello Shiny!"), i18n=i18n )
output$distPlot <- renderPlot({
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x, breaks = bins,
col = "darkgray", border = "white",
main = i18n$t("Histogram of x"), ylab = i18n$t("Frequency"))
})
}
shinyApp(ui = ui, server = server)
registerUI <- function(id, reg_title=NULL ,i18n) {
ns <- shiny::NS(id)
shiny.i18n::usei18n(i18n)
showModal(tags$div( modalDialog(title = "" ,size="s",
shiny::div(id =ns("regpanel"),
shiny::wellPanel(
shiny::tags$h2(reg_title, class = "text-center", style = "padding-top: 0;"),
shinyjs::disabled(shiny::textInput(ns("user_name1"), value= "", shiny::tagList(shiny::icon("user"), "suggested user name"))) ,#
shiny::actionButton(ns("regSubmit"), i18n$t("Submit") , class = "btn-primary", style = "color: white;")
)
)
),
easyClose = TRUE, footer = NULL ))
}
###### Module 1
register <- function(input, output, session ,title,i18n) {
ns <- session$ns
shiny::observeEvent(input$regSubmit, {
shiny.i18n::usei18n(i18n)
removeModal()
M2UI(id = ns("M2") ,reg_title=i18n$t("Hello Shiny!" ),i18n=i18n )
})
callModule(M2,id = "M2" , title= i18n$t("Hello Shiny!"),i18n=i18n)
}
###### Module 2
M2UI <- function(id, reg_title=NULL,i18n ) {
ns <- shiny::NS(id)
shiny.i18n::usei18n(i18n)
showModal(modalDialog(title = reg_title ,size="s",
shiny::wellPanel(
shiny::actionButton(ns("Finish"), i18n$t("Hello Shiny!" ) )
)
, easyClose = TRUE, footer = NULL ) )
}
M2 <- function(input, output, session ,title,i18n) {
ns <- session$ns
shiny::observeEvent(input$Finish, {
removeModal()
})
}
Did you try the latest dev version? You have to update it through the dev package. I am pretty sure it was fixed there. The problem regarded a missing callback through shiny session.
Your exampe works on my setup.
Related
I have a weird issue with conditionalPanel in shiny dashboard.
I modularized my chart UI components as I need to call it multiple times.
The conditional Panel seems to work fine if I call it only once. However, if I attempted to call more than once, it stopped working.
Below is the reproducible code:
library(shiny)
library(shinyWidgets)
library(shinydashboard)
library(shinydashboardPlus)
library(highcharter)
library(lubridate)
chartUI <- function(id) {
ns <- NS(id)
tagList(
verbatimTextOutput(ns("group")),
selectInput(ns("freq"),"Select frequency:",
choices = list("Yearly" = "Y","Half yearly" = "H","Quarterly" = "Q",
"Monthly"="M"), selected = "Yearly", multiple = FALSE),
dateInput(ns("dates"), "Select start date:",format = "yyyy-mm-dd", startview = "month", value = dmy("1/1/2014")),
selectInput(ns("link"),"Select link ratio:",choices = list("All" = "all", "Standard" = "std"),selected = "all"),
conditionalPanel("input.link == 'std'", ns=ns, sliderInput(ns("std.month"),"No of months:",min=1,max=119,value=60))
)
}
ui <- shinyUI(
ui = dashboardPagePlus(skin = "red",
header = dashboardHeaderPlus(
title = "TITLE",
titleWidth = 700
),
dashboardSidebar(),
body = dashboardBody(
# boxPlus(
# width = NULL,title = "CHART",closable = TRUE,enable_sidebar = TRUE,
# sidebar_width = 15,sidebar_start_open = FALSE,sidebar_content = chartUI("chartui1"),
# highchartOutput("")
# ),
boxPlus(
width = NULL,title = "CHART",closable = TRUE,enable_sidebar = TRUE,
sidebar_width = 15,sidebar_start_open = FALSE,sidebar_content = chartUI("chartui2"),
highchartOutput("")
)
),
title = "DashboardPage"
)
)
server <- shinyServer(function(input, output) {
})
shinyApp(ui, server)
If I only call chartui2, conditional panel works fine. But if I call both chartui1 and chartui2, both of them no longer work.
A minimal example with uiOutput / renderUI would be:
library(shiny)
dyn_ui <- function(id) {
ns <- NS(id)
tagList(selectInput(ns("show"), "show or not", choices = c("hide", "show")),
uiOutput(ns("dyn")))
}
dyn_server <- function(input, output, session) {
output$dyn <- renderUI({
ns <- session$ns
if (input$show == "show") {
sliderInput(
inputId = ns("std_month"),
"No of months:",
min = 1,
max = 119,
value = 60
)
}
})
}
ui <- basicPage(dyn_ui("test"))
server <- function(input, output, session) {
callModule(module = dyn_server, id = "test")
}
runApp(list(ui = ui, server = server))
Edit:
In fact, a minimal example works well with conditionalPanel too (see below). So something else about your app is causing a conflict. Not sure what it is, but I would start adding components one by one and see when these minimal examples start misbehaving.
library(shiny)
dyn_ui <- function(id) {
ns <- NS(id)
tagList(
selectInput(ns("show"), "show or not", choices = c("hide", "show")),
conditionalPanel(
ns = ns,
condition = "input.show == 'show'",
sliderInput(
inputId = ns("std_month"),
"No of months:",
min = 1,
max = 119,
value = 60
)
)
}
ui <- basicPage(
dyn_ui("test"),
dyn_ui("test2")
)
server <- function(input, output, session) {
}
runApp(list(ui = ui, server = server))
I am trying to use a package that allows users to graph their data in shiny (esquiss). It works fine. However the user interface for the shiny module in the package requires a fixed height container. I have therefore placed the call to the module in tag$div (inside a modal) called by a button.
The problem is that this call to this module seems to get rid of all the scrollbars for the main page of the app (so I can't scroll to the bottom of the main page (it is a one page app).
How can I limit the html of the module to prevent it from overriding the rest of the app? The code for the module being called is here.
My reproducible example follows:
ui.R
library(shiny)
library(esquisse)
library(shinyBS)
ui <- fluidPage(
dashboardPage(
dashboardHeader(title = ''),
dashboardSidebar(
sidebarMenu(
menuItem("Dashboard")
)),
dashboardBody(
actionButton(inputId = "esquissGraphs",label = "esquissGraphs"),
DT::dataTableOutput("mytable"),
bsModal("modalExample", "Data Table", "esquissGraphs", size = "large",
tags$h1("Use esquisse as a Shiny module"),
radioButtons(
inputId = "data",
label = "Data to use:",
choices = c("Mydftbbinnit", "mtcars"),
inline = TRUE
),
tags$div(
style = "height: 700px;", # needs to be in fixed height container
esquisserUI(
id = "esquisse",
header = FALSE, # dont display gadget title
choose_data = FALSE # dont display button to change data
)
)
)
)
)
)
server.R
RV <- reactiveValues(data = data.frame())
RV2 <- reactiveValues(data = data.frame())
server <- function(input, output, session) {
n<-c("1","434","101")
t<-c("Bugs","Mugs","Thugs")
RV$data<-data.frame(n,t,stringsAsFactors = FALSE)
o<-c("1","434","101")
p<-c("Bugs","Mugs","Thugs")
RV2$data<-data.frame(o,p,stringsAsFactors = FALSE)
output$mytable = DT::renderDataTable({
mtcars
})
data_r <-reactiveValues(data = data.frame())
observeEvent(input$data, {
if (input$data == "Mydftbbinnit") {
data_r$data <- RV$data
data_r$name <- "Mydftbbinnit"
} else {
data_r$data <- RV2$data
data_r$name <- "The rest"
}
})
callModule(module = esquisserServer, id = "esquisse", data = data_r)
}
shinyApp(ui, server)
You need to add
tags$style("html, body {overflow: visible !important;")
in your UI to force scrollbar to appear.
Source : https://github.com/dreamRs/esquisse/blob/master/R/esquisserUI.R
Full example gives :
library(shiny)
library(shinydashboard)
library(esquisse)
library(shinyBS)
library(shiny)
library(esquisse)
library(shinyBS)
ui <- fluidPage(
dashboardPage(
dashboardHeader(title = ""),
dashboardSidebar(
sidebarMenu(
menuItem("Dashboard")
)
),
dashboardBody(
tags$style("html, body {overflow: visible !important;"),
actionButton(inputId = "esquissGraphs", label = "esquissGraphs"),
DT::dataTableOutput("mytable"),
bsModal("modalExample", "Data Table", "esquissGraphs",
size = "large",
tags$h1("Use esquisse as a Shiny module"),
radioButtons(
inputId = "data",
label = "Data to use:",
choices = c("Mydftbbinnit", "mtcars"),
inline = TRUE
),
tags$div(
style = "height: 700px;", # needs to be in fixed height container
esquisserUI(
id = "esquisse",
header = FALSE, # dont display gadget title
choose_data = FALSE # dont display button to change data
)
)
)
)
)
)
RV <- reactiveValues(data = data.frame())
RV2 <- reactiveValues(data = data.frame())
server <- function(input, output, session) {
n <- c("1", "434", "101")
t <- c("Bugs", "Mugs", "Thugs")
RV$data <- data.frame(n, t, stringsAsFactors = FALSE)
o <- c("1", "434", "101")
p <- c("Bugs", "Mugs", "Thugs")
RV2$data <- data.frame(o, p, stringsAsFactors = FALSE)
output$mytable <- DT::renderDataTable({
mtcars
})
data_r <- reactiveValues(data = data.frame())
observeEvent(input$data, {
if (input$data == "Mydftbbinnit") {
data_r$data <- RV$data
data_r$name <- "Mydftbbinnit"
} else {
data_r$data <- RV2$data
data_r$name <- "The rest"
}
})
callModule(module = esquisserServer, id = "esquisse", data = data_r)
}
shinyApp(ui, server)
I need my Shiny module to hide/show a div outside of the namespace. I tried passing the div id to the module server function and using shinyjs to show/hide it but that is not working. I'm not getting an error, it just doesn't show/hide the div.
I know the Shiny module documentation says modules cannot access outputs outside the namespace. The docs do, though, give a way for the module to access inputs outside the namespace using reactives.
Does anyone know if there is a way for a Shiny module to access an output outside the namespace?
Here is what I'm trying to do:
### ui.R ###
header <- dashboardHeader(
title = a(href = 'http://google.com')
)
dashboardPage(
skin = 'black',
header,
dashboardSidebar(
sidebarMenu( id='tabs',
menuItem('Edit Existing Client', tabName = 'client-info')
)),
dashboardBody(
useShinyjs(),
fluidRow(
tabItems(
tabItem(tabName = "client-info",
div(selectClientModuleUI("clientinfons")),
div(id='editclientinfo', uiOutput('editclientstuff'))
)
)
)
)
)
### server.R ###
shinyServer(function(session,input, output) {
output$editclientstuff <- renderUI({
div(
fluidRow(
column(6,
textInput('editname', "Display name", value ='Testing name')
),
column(6,
numericInput('editastart','Start', value ='3')
)
)
)
})
callModule(selectClientModule, 'clientinfons', 'editclientinfo')
shinyjs::hide(id='editclientstuff')
})
### in global.R ###
selectClientModuleUI <- function(id){
ns <- NS(id)
clientlist = c(0, 1, 2)
names(clientlist) = c('Choose client', 'Fred', 'Kim')
div(
selectInput(ns('selectclient'), 'Select client to edit', choices = clientlist, selected = NULL, multiple = FALSE)
)
}
selectClientModule <- function(input, output, session, divtoshow = ''){
observeEvent(input$selectclient, {
if (!is.null(input$selectclient) && input$selectclient > 0){
print(paste0("showing ", divtoshow))
shinyjs::show(divtoshow)
}
})
}
That is possible by giving the value as a reactive (not as the value of the reactive) to the module. You can change the reactive Value in the module and return the reactive from the Module to the app (note, return the reactive itself, not its value). The following app switches the 'divtoshow' in the main app from inside the module. If nothing is selected, it's hidden, otherwise it's shown (note, I adjusted you code a little so it's working as a stand-alone app):
library(shinydashboard)
library(shinyjs)
# Module
selectClientModuleUI <- function(id){
ns <- NS(id)
clientlist = c(0, 1, 2)
names(clientlist) = c('Choose client', 'Fred', 'Kim')
div(
selectInput(ns('selectclient'), 'Select client to edit', choices = clientlist, selected = NULL, multiple = FALSE)
)
}
selectClientModule <- function(input, output, session, divtoshow){
observeEvent(input$selectclient, {
if (input$selectclient > 0){
print(paste0("showing editclientinfo"))
divtoshow("editclientinfo") # set the div to show to "editclientinfo", this will be visible outside the module
}else{
divtoshow("") # set the div to show to "", if nothing was chosen
}
})
# return the div to show as reactive to the main app
return(divtoshow)
}
# Main App
ui <- shinyUI(
dashboardPage(
skin = 'black',
dashboardHeader(
title = a(href = 'http://google.com')
),
dashboardSidebar(
sidebarMenu( id='tabs',
menuItem('Edit Existing Client', tabName = 'client-info')
)),
dashboardBody(
useShinyjs(),
fluidRow(
tabItems(
tabItem(tabName = "client-info",
div(selectClientModuleUI("clientinfons")),
div(id='editclientinfo', uiOutput('editclientstuff'))
)
)
)
)
))
server <- shinyServer(function(session,input, output) {
output$editclientstuff <- renderUI({
div(
fluidRow(
column(6,
textInput('editname', "Display name", value ='Testing name')
),
column(6,
numericInput('editastart','Start', value ='3')
)
)
)
})
# store the div to show in a reactive
divtoshow <- reactiveVal('')
# divtoshow can be changed in side this module, so it's a return value
divtoshow <- callModule(selectClientModule, 'clientinfons', divtoshow)
# observe the value of divtoshow and toggle the corresponding div
observeEvent(divtoshow(), {
if(divtoshow() == "editclientinfo"){
shinyjs::show("editclientinfo")
}else{
shinyjs::hide("editclientinfo")
}
})
})
shinyApp(ui, server)
I am developing a Shiny app with a plot (plot1 in the code) that is reactive to a data table (rhandsontable) and it displays the item selected on the table.
The table is very large so you have to scroll down to see everything. But I want the plot to be always visible, so to be fixed in the layout while you scroll down the table.
There is anyway to do it? I have done a lot of research but any answer that can help me.
My UI code is that:
ui <- dashboardPage(
dashboardHeader(title = "IG Suppliers: Tim"),
dashboardSidebar(
sidebarMenu(
menuItem("Data Cleansing", tabName = "DataCleansing", icon = icon("dashboard")),
selectInput("supplier","Supplier:", choices = unique(dt_revision_tool$Supplier)),
#selectInput("supplier","Supplier:", choices = 'Phillips'),
selectInput("segment","Segment:", choices = unique(dt_revision_tool$Segment_Name), multiple = TRUE, selected = unique(dt_revision_tool$Segment_Name)[1]),
#selectInput("segment","Segment:", choices = sgm),
selectInput("alert","Alert", choices = unique(dt_revision_tool$Alert),selected = "Yes"),
#selectInput("alert","Alert", choices = c('Yes','No'),selected = "Yes"),
selectInput("dfu","DFU", choices = c("NULL",unique(dt_revision_tool$DFU)),selected = "NULL"),
tags$hr()
# h5("Save table",align="center"),
#
# div(class="col-sm-6",style="display:inline-block",
# actionButton("save", "Save"),style="float:center")
)
),
dashboardBody(
shinyjs::useShinyjs(),
#First Tab
tabItems(
tabItem(tabName= "DataCleansing",
fluidPage(theme="bootstrap.css",
fluidRow(
plotOutput('plot1')
),
fluidRow(
verbatimTextOutput('selected'),
rHandsontableOutput("hot")
)
)
)
# #Second Tab
# tabItem(tabName = "Forecast",
# h2('TBA')
# )
)
)
)
The server code is that:
server <- shinyServer(function(input, output) {
if (file.exists("DF.RData")==TRUE){
load("DF.RData")
}else{
load("DF1.RData")
}
rv <- reactiveValues(x=dt_revision_tool)
dt <- reactiveValues(y = DF)
observe({
output$hot <- renderRHandsontable({
view = data.table(update_view(rv$x,input$alert,input$segment,input$supplier,dt$y,input$dfu))
if (nrow(view)>0){
rhandsontable(view,
readOnly = FALSE, selectCallback = TRUE, contextMenu = FALSE) %>%
hot_col(c(1:12,14),type="autocomplete", readOnly = TRUE)
}
})
})
observe({
if (!is.null(input$hot)) {
aux = hot_to_r(input$hot)
aux = subset(aux, !is.na(Cleansing_Suggestion) | Accept_Cleansing,select=c('DFU','Week','Cleansing_Suggestion',
'Accept_Cleansing'))
names(aux) = c('DFU','Week','Cleansing_Suggestion_new','Accept_Cleansing_new')
dt$y = update_validations(dt$y,aux)
DF = dt$y
save(DF, file = 'DF.RData')
}
})
output$plot1 <- renderPlot({
view = data.table(update_view(rv$x,input$alert,input$segment,input$supplier,dt$y,input$dfu))
if (nrow(view)>0){
if (!is.null(( data.table(update_view(rv$x,input$alert,input$segment,input$supplier,dt$y,input$dfu)))[input$hot_select$select$r]$DFU)) {
s = make_plot2(rv$x,(data.table(update_view(rv$x,input$alert,input$segment,input$supplier,dt$y,input$dfu)))[input$hot_select$select$r]$DFU,(data.table(update_view(rv$x,input$alert,input$segment,input$supplier,dt$y,input$dfu)))[input$hot_select$select$r]$Article_Name)
print(s)
}
}
})
})
Any help or idea will be welcome!
Thanks!
Aida
Here is an example of using CSS position: fixed to do this. You can adjust the position top and margin-top according to your requirement.
library(shiny)
ui <- shinyUI(fluidPage(
titlePanel("Example"),
sidebarLayout(
sidebarPanel(
tags$div(p("Example of fixed plot position"))
),
mainPanel(
plotOutput("plot"),
tableOutput("table"),
tags$head(tags$style(HTML("
#plot {
position: fixed;
top: 0px;
}
#table {
margin-top: 400px;
}
")))
)
)
))
server <- shinyServer(function(input, output, session) {
output$plot <- renderPlot({
plot(iris$Sepal.Length, iris$Sepal.Width)
})
output$table <- renderTable({
iris
})
})
shinyApp(ui = ui, server = server)
I'm trying to generate multiple menuItems dynamically, may be simple, but I'm not getting the right idea.
library(shiny)
library(shinydashboard)
port_tables<-c("tab1","tab2","tab3","tab4") # These are from a DB connection in the original code
function(input, output) {
output$smenu1 <- renderMenu({
sidebarMenu( id = "tabs",
h4("Tables",style="color:yellow;margin-left:20px;"),
paste("menuItem(\"",port_tables,"\",tabName=\"",port_tables,"\",icon=icon('th'))",collapse=",")
)
})
)
The menuItems from the paste function doesn't resolve( I get the result of paste function on the sidebar). I tried eval, eval(parse(paste(...))), both didn't work - what am I missing?
I couldn't quite make out what you're asking for, but here's an example of something with a dynamic menu.
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(title = "Dynamic Menu"),
dashboardSidebar(
sidebarMenuOutput(outputId = "dy_menu")
),
dashboardBody(
tabItems(
tabItem(tabName = "main",
textInput(inputId = "new_menu_name",
label = "New Menu Name"),
actionButton(inputId = "add",
label = "Add Menu")
)
)
)
)
server <- function(input, output, session){
output$dy_menu <- renderMenu({
menu_list <- list(
menuItem("Add Menu Items", tabName = "main", selected = TRUE),
menu_vals$menu_list)
sidebarMenu(.list = menu_list)
})
menu_vals = reactiveValues(menu_list = NULL)
observeEvent(eventExpr = input$add,
handlerExpr = {
menu_vals$menu_list[[length(menu_vals$menu_list) + 1]] <- menuItem(input$new_menu_name,
tabName = input$new_menu_name)
})
}
shinyApp(ui, server)
I changed the code as follows and it worked :
library(shiny)
library(shinydashboard)
port_tables<-c("tab1","tab2","tab3","tab4") # These are from a DB connection in the original code
text1<-paste("menuItem(\"",port_tables,"\",tabName=\"",port_tables,"\",icon=icon('th'))")
text2<-paste("sidebarMenu(id = 'tabs',textInput('port', 'Enter port:'),h4('Tables',style='color:yellow;margin-left:20px;'),",paste(text1,collapse=","),paste(")"))
function(input, output) {
output$smenu1 <- renderMenu({
eval(parse(text=text2))
})
)
So, the key is put the whole content of sidebarMenu in a text field and evaluate it