I'm working on a Shiny application that is supposed to handle multiple languages. I managed to dynamically translate almost all elements of the app depending on a selectInput to choose the language. However the "hard stuff" remains the navbarPage tabs as well as the tabPanels inside my pages. I cannot change their names. I tried this, but it does not work:
library(shiny)
ui <- navbarPage("App Title",
tabPanel("tab1",
selectInput("language", "language", c("EN", "FR"), width = '300px'),
textOutput("text")),
uiOutput("render_tab2"))
server <- function(input, output, session) {
output$text = renderText({ switch(input$language, "EN"="hello world", "FR"="bonjour monde") })
output$render_tab2 = renderUI({
tabPanel( title=switch(input$language, "EN"="tab2", "FR"="onglet2") )})}
shinyApp(ui, server)
And the updatenavbarpanel() family of functions are just to set the active tab, not change their characteristics...Is there a way to do it, if possible that does not change the structure of all my app... THanks a lot.
This piece of code set the title dynamically :
library(shiny)
ui <- navbarPage("App Title",
tabPanel(title = uiOutput("title_panel"),
selectInput("language", "language", c("EN", "FR"), width = '300px')
)
)
server <- function(input, output, session) {
output$title_panel = renderText({
switch(input$language, "EN"="hello world", "FR"="bonjour monde")
})
}
shinyApp(ui, server)
Edit : Works with both uiOutput("title_panel") & textOutput("title_panel")
Related
I am looking for a bit of help understanding shiny reactivity - my very basic example works, but I have a feeling it's the incorrect way to do things. I have read through the example here - which explained the different behaviours of observe versus reactive. But in my example I have used them in combination - and I would like to understand if and why, that is something I should not be doing!
My approach - is this incorrect?
I have captured the reactive value of my action button in the module (see full code below), and assigned it to my reactiveValues list using reactive().
navigation_values <- reactiveValues()
navigation_values$button1 = reactive(input$navigate)
This reactive list then gets returned to the app server, where I then access the value using navigation_values$button1(). This works (for this small example) - but is different to all the shiny documentation/vignettes, where I see instead...
observeEvent(input$navigate, {
navigation_values$button1 = input$navigate
})
And then the value accessed via navigation_values$button1
Any help appreciated! I'm not sure if this is a "philosophical" question, or if my approach would immediately cause problems if I were to extend it to a more thorough example.
Full code
library(shiny)
library(shinydashboard)
# update values module -------------------------------------
module_ui <- function(id){
ns <- NS(id)
tagList(
actionButton(inputId = ns("navigate"),
label = "Go to page 2")
)
}
module_server <- function(id){
moduleServer(id, function(input, output, session){
navigation_values <- reactiveValues()
navigation_values$button1 = reactive(input$navigate)
return(navigation_values)
})
}
# UI --------------------------------------------
sidebar <- dashboardSidebar(
sidebarMenu(
id = "mastertabs",
menuItem(
text = "Page 1",
tabName = "page1"),
menuItem(
text = "Page 2",
tabName = "page2")))
body <- dashboardBody(
tabItems(
tabItem(
tabName = "page1",
fluidPage(module_ui("mymodule"))
)))
ui <- dashboardPage(
dashboardHeader(title = "PRACTICE"),
sidebar,
body
)
# Server ------------------------
server <- function(input, output, session){
navigation_values <- module_server("mymodule")
observeEvent(navigation_values$button1(), {
updateTabsetPanel(session, inputId = "mastertabs", selected = "page2")
})
}
shinyApp(ui = ui, server = server)
I think the best approach is simply:
moduleServer(id, function(input, output, session){
return(reactive(input$navigate)
})
Then, in your app, just call:
navigation_values()
I'm very new to shiny or any dashboard related development for that matter. My question is kind of specific and only tackles how the data is displayed and does not talk about internal logic of the app.
I applied language = "ru" option inside dateRangeInput and it changed how the text within date selector is rendred but it did not change the "to" bettween the two datefields. I understand that it might sound like I'm nitpicking but these little details matter for what I'm trying to do. Screenshow to ilustrate the problem.
Thanks in advance!
ui <- fluidPage(
titlePanel("Калькулятор остатков"),
sidebarLayout(
sidebarPanel(
dateRangeInput("dates","Временной диапазон",start = "2020-07-01",
end = "2020-09-01", language = "ru"),
),
mainPanel()
)
)
server <- function(input, output, session) {
}
shinyApp(ui, server)
The function dataRangeInput has an seperator- argument, the default is to. You may change this to your desired word.
ui <- fluidPage(
titlePanel("Калькулятор остатков"),
sidebarLayout(
sidebarPanel(
dateRangeInput("dates",
"Временной диапазон",
start = "2020-07-01",
end = "2020-09-01",
language = "ru",
separator = " до "),
),
mainPanel()
)
)
server <- function(input, output, session) {
}
shinyApp(ui, server)
I want to jump to a certain page of an embedded PDF in shiny. I use tags$iframe to display the PDF. I know that I have to expand the URL in tags$iframe to jump to a certain page of the PDF by adding #page=x, i.e. tags$iframe(style="height:785px; width:100%", src="http://www.pdf995.com/samples/pdf.pdf#page=3").
However, if I have multiple tabs and switch from tab 1 to tab 2 and back to tab 1, the PDF always shows page 1. I could reload the whole tab/PDF to jump back to page 3, but I don't want to do that!
I tried to use JavaScript but it doesn't work because document.getElementById doesn't work properly.
My code so far
library(shiny)
library(shinyjs)
ui <- tagList(
useShinyjs(),
tags$script('Shiny.addCustomMessageHandler("go_to_page", function(message) {
document.getElementById("show_pdf").contentWindow.PDFViewerApplication.page = 3;
});'),
fluidPage(
fluidRow(
column(6,
tabsetPanel(id = "tabs",
tabPanel(
"Tab 1",
uiOutput("show_pdf")
),
tabPanel(
"Tab 2",
uiOutput("show_pdf1"))
)
)
))
)
server <- function(input, output, session){
output$show_pdf <- renderUI({
tags$iframe(style="height:785px; width:100%", src="http://www.pdf995.com/samples/pdf.pdf#page=3")
})
output$show_pdf1 <- renderUI({
tags$iframe(style="height:785px; width:100%", src="http://www.pdf995.com/samples/pdf.pdf#page=4")
})
observe({
input$tabs
session$sendCustomMessage(type = 'go_to_page', message = runif(1))
})
}
shinyApp(ui = ui, server = server)
What do I have to change so that the code works properly?
writing with a shiny question. I have a navbarPage, id = "navbar", and in the navbarMenu user can select one among several tabPanels. Each tabPanel is assigned a value (value = 1, value = 2, etc). So input$navbar is reactive value with the value of the selected tabPanel.
I have a reactive expression defined which reacts to the changing of the tabPanel (reacts based on input$navbar). What I actually want is for this to react to navigating to a particular tabPanel, but not navigating away from that tabPanel. So, when input$navbar changes from 1 to 2 I want a reaction, but when changing from 2 to 1 no reaction. How can I achieve this?
Here is relevant snippet of my code, I don't think I need a full reproducible example for this but let me know if I'm wrong.
#ui snippet
navbarPage(id = "navbar",
navbarMenu(title = "Title",
tabPanel(title = "tp1", value = 1),
tabPanel(title = "tp2", value = 2),
#more tabPanels and ui stuff...
#server snippet
rctvfx <- reactive({
#want this to react only when input$navbar changes from ==1 to ==2
input$navbar
isolate({
#do stuff
})
})
You can use an if statement. This makes sure the code only runs if the user navigated to the corresponding tab.
library(shiny)
shinyApp(
ui = navbarPage(
"App Title",
id = "navbar",
tabPanel("Plot"),
navbarMenu(
"More",
tabPanel("Summary"),
"----",
"Section header",
tabPanel("Table")
)
),
server = function(input, output){
observe({
if (req(input$navbar) == "Table")
message("Table has been selected")
if (req(input$navbar) == "Plot")
message("Plot has been selected")
})
}
)
I would recomment do use observe rather than reactive to make sure everything runs even if all observers for the reactive are idle.
Another example of the same answer as above
library(shiny)
shinyApp(
ui = navbarPage(
"App Title",
id = "navbar",
tabPanel("Plot"),
navbarMenu(
"More",
tabPanel("Summary"),
tabPanel("Table"),
mainPanel(dataTableOutput("d"))
)
),
server = function(input, output){
output$d = renderDataTable({
if ((input$navbar) == "Table") {
head(mtcars)
} else {
((input$navbar) == "Plot")
head(iris)
}
})
}
)
I have a question about conditional generation of navbarMenu based on the variable in the server-side. I created a small demo app to illustrate it.
ui = shinyUI(
navbarPage(title = "Demo app",
navbarMenu("Small numbers",
tabPanel("First small page", uiOutput("firstSmallPage"))
),
navbarMenu("Big numbers",
tabPanel("First big page", uiOutput("firstBigPage"))
)
)
)
server = shinyServer(function(input, output, session) {
rand_num = sample(1:10)[1]
# if rand_num is higher than 5 I dont want Big number navbarMenu to appear
print(rand_num)
output$firstSmallPage <- renderUI({
plotOutput("smallPlot")
})
output$smallPlot <- renderPlot({plot(1:10)})
output$firstBigPage <- renderUI({
plotOutput("bigPlot")
})
output$bigPlot <- renderPlot({plot(990:1000)})
})
app = shinyApp(ui=ui, server=server)
What I am trying to archive is to hide Big Numbers tab if rand_num is higher 5. I tried wrapping navbarMenus in renderUI on the server-side and replacing it with uiOutput in the ui-side but it was unsuccessful. It is crucial for solution to work with more than 2 navbarMenus. Thanks in advance
It seems to work like this:
library(shiny)
library(shinyjs)
ui = shinyUI(
navbarPage(
useShinyjs(),
title = "Demo app",
navbarMenu("Small numbers",
tabPanel("First small page", uiOutput("firstSmallPage"))
),
navbarMenu("Big numbers",
tabPanel("First big page", uiOutput("firstBigPage"))
)
)
)
server = shinyServer(function(input, output, session) {
rand_num = sample(1:10)[1]
# if rand_num is higher than 5 I dont want Big number navbarMenu to appear
print(rand_num)
if(rand_num>5){
hide(selector = ".navbar-nav li:nth-child(3)")
}
output$firstSmallPage <- renderUI({
plotOutput("smallPlot")
})
output$smallPlot <- renderPlot({plot(1:10)})
output$firstBigPage <- renderUI({
plotOutput("bigPlot")
})
output$bigPlot <- renderPlot({plot(990:1000)})
})
app = shinyApp(ui=ui, server=server)
runApp(app)