Apply a req statement to multiple outputs - r

Is there any way to apply a req command to multiple output objects in a shiny app without having to repeat the req statement each time? In the example below, you will see that the req statement is repeated in both the part1 and part2 outputs.
library(shiny)
ui <- fluidPage(
textInput("commentText", "Input Text"),
textOutput(outputId = "part1"),
textOutput(outputId = "part2")
)
server <- function(input, output, session) {
output$part1 <- renderText({
req(input$commentText)
"Hello"
})
output$part2 <- renderText({
req(input$commentText)
"World!"
})
}
shinyApp(ui, server)
If possible, I would only like to have to input the req statement once (the actual use case has 6 different objects tied to the req so I'd like to avoid repetition if possible). I tried the following approach using an observer, but this fails when you type in and then delete text (when the text is deleted, 'HelloWorld!' still appears).
server <- function(input, output, session) {
observeEvent(input$commentText, {
req(input$commentText)
output$part1 <- renderText({
"Hello"
})
output$part2 <- renderText({
"World!"
})
})
}

It is not recommended to nest a render function inside an observer. Instead, you could use a renderUI and handle both texts.
req allows more than one argument. You can check several inputs in one statement.
req(..., cancelOutput = FALSE)
... Values to check for truthiness.
Attempted solution:
library(shiny)
ui <- fluidPage(
textInput("commentText", "Input Text"),
textInput("commentText2", "Input More Text"),
uiOutput(outputId = "part1and2")
)
server <- function(input, output, session) {
output$part1and2 <- renderUI({
req(input$commentText, input$commentText2)
tagList(
wellPanel("Hello"),
wellPanel("World!")
)
})
}
shinyApp(ui, server)

Related

How to pass Shiny browser timezone value from ui to server and readTzServer

I have a modularized application with two "ui" functions (ui and readTzUi) and two "server" functions. For the application I want to read the timezone from a users browser with readTzUi (input#client_time_zone_international) and pass it on to readTzServer.
I have worked out a reproducable example of the app.
As you can see the but_out and out variable can be accessed through the readTzServer, but input$client_time_zone_international results in a NULL value.
Although input$client_time_zone_international is not available in readTzServer, it is available in server (The values of timezone are printed in the console for both readTzServer and server)
A few things that I have tried so far are:
pass input$client_time_zone_international from server to readTzServer with Callmodule
create a global variable of input$client_time_zone_international
capture input$client_time_zone_international in a variable from the readTzUi with Shiny.setInputValue()
All these options did not result in passing the input$client_time_zone_international value to readTzServer.
I hope someone can help me with this problem.
library(shiny)
readTzUi <- function(id, label = "readTz"){
ns <- NS(id)
fluidPage(
tags$script('
$(function() {
$("input#client_time_zone_international").val(Intl.DateTimeFormat().resolvedOptions().timeZone)
});
'),
textInput("client_time_zone_international", "Time zone international", value = ""),
tags$br(),
actionButton(ns("button"), label = label),
verbatimTextOutput(ns("but_out")),
verbatimTextOutput(ns("out"))
)
}
readTzServer <- function(id){
moduleServer(
id,
function(input, output, session){
# This is where I need the timezone value
observe(print(input$client_time_zone_international))
count <- reactiveVal(0)
observeEvent(input$button, {
count(count() + 1)
})
output$but_out <- renderText({
count()
})
count
observe({
output$out <- renderText({
"Hello"
})
})
}
)
}
ui <- fluidPage(
readTzUi("readtz1", "Counter#2")
)
server <- function(input, output, session) {
readTzServer("readtz1")
observe(
print(input$client_time_zone_international)
)
}
shinyApp(ui, server)
This is example how to get a timezone:
library(shiny)
ui <- basicPage(
tags$script("$(document).on('shiny:sessioninitialized', function(event) {
var n = Intl.DateTimeFormat().resolvedOptions().timeZone;
Shiny.onInputChange('client_time', n);});")
)
server <- function(input, output, session) {
observe({
req(input$client_time)
print(input$client_time)
})
}
shinyApp (ui = ui, server = server)

R Shiny Link Multiple Inputs to Control 1 Output

I have a shiny App where I am displaying the same output multiple times. I have two inputs and they need to both control the same output. In my example below the outputs are copies of each other and it has to stay that way. Currently only the first input does anything. I need them to control the same output and react to changes in each other.
ui <- function(request) {
fluidPage(
textInput("txt1", "Enter text1"),
textInput("txt1", "Enter text2"),
checkboxInput("caps", "Capitalize"),
verbatimTextOutput("out1"),
verbatimTextOutput("out2"),
)
}
server <- function(input, output, session) {
output$out2<- output$out1 <- renderText({
if (input$caps)
toupper(input$txt1)
else
input$txt1
})
}
shinyApp(ui, server, enableBookmarking = "url")
You need to give your inputs unique IDs, but in your code both IDs are txt1. If you change this, you can use the normal reactivity:
library(shiny)
ui <- function(request) {
fluidPage(
textInput("txt1", "Enter text1"),
textInput("txt2", "Enter text2"),
checkboxInput("caps", "Capitalize"),
verbatimTextOutput("out1"),
verbatimTextOutput("out2"),
)
}
server <- function(input, output, session) {
output$out2<- output$out1 <- renderText({
if (input$caps)
paste(toupper(input$txt1), toupper(input$txt2))
else
paste(input$txt1, input$txt2)
})
}
shinyApp(ui, server, enableBookmarking = "url")
I have had a similar situation where I needed multiple identical inputs (albeit I only needed one output) that always have the same value.
The solution for me was to create a reactive element that holds the value for the inputs and syncs the value with the inputs.
Ie this code always makes input 1 and 2 have the same values
library(shiny)
ui <- fluidPage(
selectInput("id1", "Input 1", choices = c("A", "B")),
selectInput("id2", "Input 2", choices = c("A", "B")),
)
server <- function(input, output, session) {
# the reactive value always holds the value from the inputs
input_filter <- reactiveVal("A")
# sync from the reactive value to the inputs
observeEvent(input_filter(), {
print("input_filter() has changed")
updateSelectInput(session, "id1", selected = input_filter())
updateSelectInput(session, "id2", selected = input_filter())
})
# sync from the inputs to the reactive value
observeEvent(input$id1, {
print("Update id1")
input_filter(input$id1)
})
observeEvent(input$id2, {
print("Update id2")
input_filter(input$id2)
})
}
shinyApp(ui, server)

R Shiny won't output a variable using textOutput

I'm trying to write a calculator using Shiny in R for a video game (you input the stats of you and your opponent, and it outputs your odds of winning a match). However, I can't get the Shiny app to output any of my variables. The app runs fine, but nothing outputs when the action button is selected.
Trying to find the issue, I simplified my code into a basic calculator that takes a numeric input, multiplies it by two, and outputs a result. As before, nothing is displayed when the action button is pushed. However, if you directly type a string into the renderText function, it works just fine.
I need to include an action button in my ultimate code because I don't want it to calculate the result until several numerical values have been typed in. Could the action button be causing an issue somewhere, or is it something else?
Below is the simplified code. If I can get this to run, I'm sure I could get my more complicated code to run. Thank you!
library(shiny)
library(shinythemes)
ui <- fluidPage(
titlePanel("Multiply by 2"),
fluidRow(
column(12, textOutput("test"),
numericInput(inputId = "start", "Start", value = 1),
actionButton("go", "Go!") )
)
)
server <- function(input, output) {
myval <- reactiveValues()
observeEvent(input$go, {
reactive ({
if (input$go == 0)
return()
isolate({
myval$calc <- paste("The result is", 2*input$start)
})
})
})
output$test <- renderText({
if (input$go == 0)
return()
isolate({
myval$calc
})
})
}
shinyApp(ui = ui, server = server)
It looks like there is some extra code in there we don't need, for example the isolate function. See the below minimal example:
input$go doesn't tell us what the button is doing. Try running print(input$go) and have a look at the output.
library(shiny)
ui <- fluidPage(
titlePanel("Multiply by 2"),
fluidRow(
column(12,
textOutput("test"),
numericInput(inputId = "start", "Start", value = 1),
actionButton("go", "Go!")
)
)
)
server <- function(input, output) {
myval <- reactiveValues()
#Observe button (will run when the button is clicked)
observeEvent(input$go, {
myval$calc <- paste("The result is", 2 * input$start)
})
#Text output (will run when myval$calc changes)
output$test <- renderText({
myval$calc
})
}
shinyApp(ui = ui, server = server)

How to render something first in shiny before excuting the rest of code?

I want to render a text to notify the user that a task is going to run, but it seems that shiny executes all code in server first then it moves to UI.
Here is an example:
library(shiny)
ui <- fluidPage(
mainPanel(
textOutput("ptext")
))
server <- function(input, output) {
output$ptext <- renderText("creating a dataframe")
df <- matrix(rnorm(10000),nrow = 10) # a large dataset
output$ptext <- renderText("dataframe created !!")
}
shinyApp(ui = ui, server = server)
In the above example, I never see "creating a dataframe", How to render that text first before executing the rest of the code.
It's not the most beautiful, but if you can use an input for status messages like this, you can relay what's going on ...
library(shiny)
ui <- fluidPage(
mainPanel(
textInput("notice", "Status", "creating a dataframe"),
textOutput("ptext")
)
)
server <- function(input, output, session) {
dat <- reactive({
Sys.sleep(3)
matrix(rnorm(10000), nrow = 10)
})
output$ptext <- renderText({
req(dat())
updateTextInput(session, "notice", value = "dataframe created !!")
return("hello world")
})
}
shinyApp(ui = ui, server = server)
(Note the addition of session to the arguments to server, necessary to use updateTextInput(session, ...).)
You could get more complex by using dynamic UI creation and deletion, or object hiding (perhaps using shinyjs), but that is getting a bit more complex than I think you may want.

Shiny Modules not working with renderUI

I am using renderUI to optionally present a Table or Plot based on user selection of the visualization option. I am also using Shiny modules to present the same thing on multiple tabs. While I have gotten Shiny modules to work wonderfully in another app, I am struggling to get it to work with renderUI.
Here is a minimal piece of code that I came up with that shows the problem where nothing gets displayed on either tabs:
myUI <- function(id) {
ns <- NS(id)
fluidRow(
uiOutput(ns('myFinalText'))
)
}
ui <- fluidPage(
tabBox(id = 'myBox', width = 12,
tabPanel('Tab1',
fluidRow(
myUI('tab1')
)),
tabPanel('Tab2',
fluidRow(
myUI('tab2')
))
)
)
myTextFunc <- function(input, output, session, text) {
output$myFinalText <- renderUI({
output$myText <- renderText({text})
textOutput('myText')
})
}
server <- function(input, output, session) {
callModule(myTextFunc, 'tab1', session = session, 'Hello Tab1')
callModule(myTextFunc, 'tab2', session = session, 'Hello Tab2')
}
shinyApp(ui = ui, server = server)
Any thoughts on what else I should be doing to make this work?
Replacing the Shiny module UI function and server functions as follows makes it work fine.
myUI <- function(id) {
ns <- NS(id)
fluidRow(
textOutput(ns('myFinalText'))
)
}
myTextFunc <- function(input, output, session, text) {
output$myFinalText <- renderText({
text
})
}
You can get the namespace from the session object. Change myTextFunc in the initial app like this:
myTextFunc <- function(input, output, session, text) {
ns <- session$ns
output$myFinalText <- renderUI({
output$myText <- renderText({text})
textOutput(ns('myText'))
})
}
You shouldn't call output$ function from another output$ function - it's against Shiny design patterns.
output$myFinalText <- renderUI({
output$myText <- renderText({text})
textOutput(ns('myText'))
})
If you want to know, why it is very bad practice, watch Joe Cheng tutorial about 'Effective reactive programming' from this site: https://www.rstudio.com/resources/webinars/shiny-developer-conference/.
You should use rather reactiveValues or reactive expressions instead. What exactly you should use is dependent from what do you want to achieve, so it's hard to say without detailed example, but according to Joe Cheng everything can be accomplished without nesting outputs or observers.
Sorry for answering my own question...but for others looking for a similar solution, this may be of help.
Here is how I solved for the need to inherit Shiny module namespace on the server side to dynamically render UI. IF there is a better way to solve, please comment or post.
tab1NS <- NS('tab1')
tab2NS <- NS('tab2')
myUI <- function(ns) {
tagList(
fluidRow(
radioButtons(ns('type'), 'Select Visual:',
choices = c('Table' = 'table',
'Plot' = 'plot'))
),
fluidRow(
uiOutput(ns('myCars'))
)
)
}
ui <- fluidPage(
tabBox(id = 'myBox', width = 12,
tabPanel('Tab1',
fluidRow(
myUI(tab1NS)
)),
tabPanel('Tab2',
fluidRow(
myUI(tab2NS)
))
)
)
myTextFunc <- function(input, output, session, cars, ns) {
getMyCars <- reactive({
if (input$type == 'table') {
output$table <- renderDataTable({datatable(cars)})
dataTableOutput(ns('table'))
} else{
output$plot <- renderPlot({
plot(cars$wt, cars$mpg)
})
plotOutput(ns('plot'))
}
})
output$myCars <- renderUI({
getMyCars()
})
}
server <- function(input, output, session) {
callModule(myTextFunc, 'tab1', session = session,
mtcars[mtcars$am == 1, ], tab1NS)
callModule(myTextFunc, 'tab2', session = session,
mtcars[mtcars$am == 0, ], tab2NS)
}
shinyApp(ui = ui, server = server)
Replacing your functions with this renderUI equivalent also works:
myUI <- function(id) {
ns <- NS(id)
fluidRow(
uiOutput(ns('myFinalText'))
)
}
myTextFunc <- function(input, output, session, text) {
output$myFinalText <- renderUI({
text
})
}
Although this obviously does not capture the complexity of what you are really doing. There's something not right about using output$... and textOutput within the renderUI like that. I don't think that is necessary - you don't actually have to use the textOutput function to include text in your output.
EDIT: It occurs to me that the problem has to do with namespaces and modules. When you do output$myText <- renderText(text), the result ends up in the namespace of tab1 or tab2. For example, try changing your textOutput to
textOutput('tab1-myText')
and watch what happens. I think this is why having output$.. variables in your renderUI is problematic. You can access inputs via callModule and that should take care of any namespace issues.

Resources