Input data reactivity freezes in Shiny module - r

When executing module innerDemo function (see code below), input data is not correctly reflected in the result displayed. I executed innerDemo() and was expecting displayed data field to reflect exactly what I typed in Quantity field. But displayed data field stopped moving according to entry at some point (see picture).
So it seems that reactivity stopped working after some time.
Either the code below is flawed or my R configuration is not correct. I run latest versions of R and Shiny though.
Also I follow the latest module code skeleton format (as per "Mastering Shiny" book) but all questions and answers on this forum use the older module code skeleton format (with callModule), so I am confused.
Innerdemo execution result
library(shiny)
innerUI <- function(id) {
tagList(
numericInput(NS(id,"Qty"),label="Quantity:",value=0)
)
}
innerServer <- function(id) {
moduleServer(id, function(input,output,session) {
data <- reactive({input$Qty})
print(paste0("Data:Qty ",data()))
return(data)
})
}
innerDemo <- function() {
ui <- fluidPage(
innerUI("Ex1"),
tableOutput("Ex")
)
server <- function(input,output,session) {
output$Ex <- renderTable(innerServer("Ex1")())
}
shinyApp(ui,server)
}
R.version shows R4.2.2, shiny version is 1.7.4 and all dependencies are up to date.
Thanks for any idea,
Alexis

Related

Shiny - How to test fileInput()

I'm trying to write tests in order to check if a shiny function fileInput() is reading files correctly.
My problem is that I don't know what to write in session$setInputs() in order to grab the file from my system.
Here is an example app:
library(shiny)
ui <- fluidPage(
tagList(
fileInput("file", "Please upload a file"),
tableOutput("text")
)
)
server <- function(input, output, session){
file <- reactive({input$file})
output$text <- renderTable({
req(file())
read.csv(file()$datapath)
})
}
shinyApp(ui, server)
Now, I want to be able to use testServer() in order to set a file address and see if my app loads it correctly, but I can't figure out how to do it:
address <- "path/to/text.csv"
testServer(server, {
session$setInputs(file = address)
print(file())
})
I think it has to do with the fact that fileInput() uploads the file to a temp folder and returns to shiny a dataframe where you can get the datapath, but I'm unable to simulate this pass in order to make the test work
I have the same question as you do, I did some investigating and could not find any way of testing fileInput with testServer or testthat. The best solution that I found was testing fileInput by taking a snapshot when recording a test with recordTest() of the shinytest package.
Sorry for answering this late.
I asked the same question at rstudio's forums and got an answer here
The basics of it are setting the file's datapath as a list:
address <- "path/to/text.csv"
testServer(server, { session$setInputs(file= list(datapath = address)) })

Testing modular R Shiny (golem) dashboards

I've been exploring (**and loving) the golem package for developing modular dashboards with R Shiny. But I'm struggling to wrap my head around how to test a modular dashboard.
For example in the repex below how would I test that if the input$n_rows in the import module is set to 15 that the output in the display module contains 15 rows?
I'd be incredibly grateful for any support on this!
library(shiny)
library(reactable)
library(dplyr)
# Import module UI
mod_import_ui <- function(id){
ns <- NS(id)
fluidRow(
# Allow the user to select the number of rows to view
numericInput(ns("n_rows"),
"Select number of observations",
value = 10)
)
}
# Import module Server
mod_import_server <- function(id){
moduleServer(
id,
function(input, output, session){
data <- reactive({
# Sample the requested number of rows from mtcars and return this to the application server
mtcars %>%
slice_sample(n = input$n_rows)
# [....] # Some complex formatting and transformations
})
return(data)
}
)}
# Display module UI
mod_display_ui <- function(id){
ns <- NS(id)
fluidRow(
reactableOutput(ns("table"))
)
}
# Display module Server
mod_display_server <- function(id, data_in){
moduleServer(
id,
function(input, output, session){
# [....] # Some more transformations and merging with data from other modules
output$table <- renderReactable(reactable(data_in()))
}
)}
app_ui <- function(request) {
tagList(
mod_import_ui("import_1"),
mod_display_ui("display_1")
)
}
app_server <- function(input, output, session) {
data_in <- mod_import_server("import_1")
mod_display_server("display_1", data_in)
}
shinyApp(ui = app_ui, server = app_server)
I would recommend separating the core of the app from the user interface.
The {golem} framework allows building your application inside a R package, which means you can use all tools from package build to document and test your code.
If you follow our guide in engineering-shiny.org/, you will see that we recommend extracting all R code from your "server" part to test it in a vignette, transform it as a regular function, so that you can test it as usual with R packages.
Hence, you ShinyApp only calls internal functions, already documented and tested. With this approach, you can test outputs of different scenarios that can happen in your application. Try different input parameters in a static script and verify outputs, whatever you change in your app in the next steps of your development.
The book gives a lot of advices. If I had to sum them up for a workflow, this would be:
Build the necessary code in an Rmd directly. This allows you to test the operation without having to go through all the necessary clicks. We call it the "Rmd first" method: https://rtask.thinkr.fr/when-development-starts-with-documentation/
Factorize this code into R functions to put as little as possible in the Shiny app itself.
Create your UI part without server (or not too much), just to see what the general appearance looks like
Include your functions in the appropriate places in the app.
Strengthen the code. Reproducible examples, unit tests, documentation, code versioning, ... (This step is better when done in parallel with the code)
As a complement to Sebastien's answer, I would like to point that starting with {shiny} v 1.5.0, you can test server functions directly using the testServer function, which might be what you are looking for.
Here is a reprex of how you can achieve that:
library(shiny)
library(magrittr)
library(dplyr)
mod_import_server <- function(id){
moduleServer( id, function(input, output, session){
data <- reactive({
mtcars %>%
slice(n = 1:input$n_rows)
})
return(data)
})
}
shiny::testServer(mod_import_server, {
for (i in 1:10){
session$setInputs(n_rows = i)
testthat::expect_equal(
data(),
slice(mtcars, n = 1:i)
)
}
})
Here, you can test that your reactive() behave the way you're expecting.
That's not perfect but a good start :)
It's hard to find a way to test for the behaviour of the second module though, as it depends on a reactive() passed as a value.
Colin

Render table R Shiny upon action button

I have been using R for about a year but am only a few weeks into Shiny. I have some code which calls an API and spits out a nice tables of results which I would like to use in a Shiny App - with the API being called when an action button is clicked.
I've been trouble shooting this for a few days but can't figure it out, below are a few of the things I have tried.
If I remove the action button, the API calls and displays as soon as the app is opened.
Replacing the API with simple text shows the text when the button is clicked.
Rendering the table inside the action results in the UI wondering where the table is (as button as not been pressed) and giving an error.
The API contains sensitive info so I have removed some details added a typical return (it returns Json to the browser when URL is visited).
Any ideas on what i might be missing?
API return
{"meta":{"request":{"granularity":"Monthly","main_domain_only":false,"format":null,"domain":"cnn.com","start_date":"2017-02-01","end_date":"2017-02-28","limit":null,"country":"world"},"status":"Success","last_updated":"2017-04-30"},"visits":[{"date":"2017-02-01","visits":100000`}]}
UI.r
library(shiny)
shinyUI(fluidPage(
headerPanel(actionButton("do", "Click Me")),
mainPanel(dataTableOutput("mytable"))
))
Server.r
library(shiny)
function(input, output) {
#Call API, flatten Json return
Visits_Final<- eventReactive(input$do, {
Results<- paste0("https://api.com/",
"site.com",
"apikey=***") %>%
map(fromJSON, flatten = FALSE)
#Transform into DF
visits_temp= data.frame(date=NA,visits=NA,ID=NA)
for(i in 1:1){
DF_L<- as.data.frame(Results[[i]]$visits)
ID <- (rep(i, nrow(DF_L)))
DF_L<- cbind(DF_L, ID)
Visits_Final<-rbind(visits_temp, DF_L)}})
#Output to UI
output$mytable <- renderDataTable({Visits_Final()})
}
Thanks in advance!
Edits
for(i in 1:i){
As per BigDataScientist comment, changed to
for(i in 1:1){
Code comments added
System info added:
R 3.3.2
R Studio Version 1.0.143
OS Sierra 10_12_3
Solved - In the Server file I changed eventReactive to observeEvent
Honestly, not 100% understanding this documentation but it did help
Shiny: what is the difference between observeEvent and eventReactive?
Feel free to comment with similar problems.

Shiny reactive UI not running server code

I'm trying to get a Shiny reactive UI running. It is getting quite complex (in terms of lines of code) so I thought refactoring was a good idea. To put it short, this is my server code:
require(ggplot2)
require(h2o)
shinyServer(function(input, output, session) {
#stop()
localH2o <<- h2o.init(nthreads = 3) #Global variable
source("BuilderServer.R", local = TRUE)[1]
source("ReviewerServer.R", local = TRUE)[1]
# CleanupFUnctions
session$onSessionEnded(function() {
rm(list=ls())
})
})
where I assumed source with local = TRUE was just like copy-paste the content of the R files. So they contain functions of the form output$functionName <- renderUI({code}). The ui code depends on these functions, most of them are reactive, the ui code looks like this:
shinyUI(navbarPage("Metamodel",
tabPanel("Build Custom Model",
fluidRow(
column(12,align="center",
uiOutput("BuilderUpTitle")
)
),
fluidRow(
column(3,
uiOutput("BuilderAxisSelector")
)
)
)
))
In this particular case, the "BuilderUpTitle" function looks like this:
output$BuilderUpTitle <- renderUI({
inFile <- input$BuilderInputFile
if(is.null(input$BuilderInputFile)){
fileInput("BuilderInputFile", "Upload a xlsx file")
} else {
#R Stuff done here with the file
textInput("text", label = h3("Model Title"), value = "Enter text...")
}
})
I wrote the code yesterday and it was working. Today I turned on the computer again, and when launching the app, not even the dependencies from the server.R appear to load (ggplot2 and h2o). The download button from the "BuilderUpTitle" function doesn't appear at all and shiny appears to only execute the ui.R code. I set the workspace to the folder of the sourcefiles and it doesn't help. Even if I uncomment the stop() function from the server, nothing seems to change. Setting breakpoints in RStudio doesn't stop the code inside the server, so that is why I think shiny is not calling the server function. However, the code was working before and I did not modify a single file. I even copied the content of the source files to the server.R code and still they do not load. Is there something obvious that I'm missing? Thank you in advance!
Ok, once again, I found myself what the problem is, and none of the things I said would've made anyone find what was wrong. Here is the tiniest possible code that reproduces the problem:
ui.R
shinyUI(fluidPage(
fluidRow(
uiOutput("itWillLock"),
uiOutput("itWillLock")
)
))
server.R
shinyServer(function(input, output) {
output$itWillLock <- renderUI({
sliderInput("slider","Slider",min=0,max=1,value=0)
})
})
I guess R gets stuck in an infinite loop and never reaches the server.R file. Is this a bug that I should report? Or just common sense will keep people out of this trouble. Thank you!

How to manage my R code in a shiny or shinydashboard app?

I have made a shinydashboard app which has now quite an amount of lines of code, and I am wondering if there are ways to split the code into different . R files. I have seen a similar question here, but the answer does not help (especially it says nothing about the code in the server part of the app).
For the ui part, I have created functions called header, sidebar, and body, and then I merely write
ui <- dashboardPage(header(), sidebar(), body())
It works well, and it still works if the functions header, sidebar, and body need to have arguments.
For the server part, I don't think a similar strategy can be applied. I am wondering whether "local" server functions (one per menu item for instance) could be written, and then reunified into one central server function.
Do you think something like that is doable? More generally, thank you for your advice and ideas that could make my code more manageable.
I am not sure if this meets your requirement, you can create different files and do the required computations in those files and save all the objects (data frames or lists or literally anything) into .Rds file using saveRDS() in R and then load that file into server.R using loadRDS() which will have all your saved objects. You can find the documentation here.
Then simply use those objects by calling the names as they are saved earlier. Most of the complex Shiny apps use global.R file (just a general convention, you can use any name) to do the heavy computations and follow the above approach.
You can always use source to call other R files in server.R:
Use source as you usually do in regular R outside any reactive functions.
Use source("xxxxx", local=T) when you want to use it inside a reactive function so the r codes you called will run every time this piece of reactive codes are activated.
For the server side:
server.R:
library(shiny)
source('sub_server_functions.R')
function(input, output, session) {
subServerFunction1(input, output, session)
subServerFunction2(input, output, session)
subServerFunction3(input, output, session)
}
This has worked for me, it's possible you'll need to pass more variables to the subserver functions. But the scoping of the reactive output appears to allow this.
sub_server_functions.R:
subserverfunction1 <- function(input, output, session) {
output$checkboxGroupInput1 <- renderUI({
checkboxGroupInput('test1','test1',choices = c(1,2,3))
})
}
subserverfunction2 <- function(input, output, session) {
output$checkboxGroupInput2 <- renderUI({
checkboxGroupInput('test2','test2',choices = c(1,2,3))
})
}
subserverfunction3 <- function(input, output, session) {
output$checkboxGroupInput3 <- renderUI({
checkboxGroupInput('test3','test3',choices = c(1,2,3))
})
}

Resources