How to run user input as R code in a Shiny app? - r

I want to create a shiny application that has an input for writing some R function or Command, reads it through the ui.R then passes it to the server.R that executes that R command to display the results.
I spent hours searching about some example but couldn't find anything, I already know how to create Shiny apps using ui and server and pass the input values to server and work with them, but I have no idea if it's possible to create a shiny app like R where you can write the commands and return the results, any example or help would be appreciated.

Letting users run code in your app is bad practice, since it comes with great security risks. However, for development you might want to check this function from the shinyjs package by Dean Attali.
Example from the link:
library(shiny)
library(shinyjs)
shinyApp(
ui = fluidPage(
useShinyjs(), # Set up shinyjs
runcodeUI(code = "shinyjs::alert('Hello!')")
),
server = function(input, output) {
runcodeServer()
}
)
Some examples of why it is not such a good idea to include when deploying your app:
Try the input:
shinyjs::alert(ls(globalenv()))
or
shinyjs::alert(list.files())

I was able to find an alternative solution that doesn't require shinyjs -- wanted to restate Florian's concern that in general it is not a good thing (not secure) to let users run code in your Shiny app. Here is the alternative:
library(shiny)
library(dplyr)
ui <- fluidPage(
mainPanel(
h3("Data (mtcars): "), verbatimTextOutput("displayData"),
textInput("testcode", "Try filtering the dataset in different ways: ",
"mtcars %>% filter(cyl>6)", width="600px"),
h3("Results: "), verbatimTextOutput("codeResults"))
)
server <- function(input, output) {
shinyEnv <- environment()
output$displayData <- renderPrint({ head(mtcars) }) # prepare head(mtcars) for display on the UI
# create codeInput variable to capture what the user entered; store results to codeResults
codeInput <- reactive({ input$testcode })
output$codeResults <- renderPrint({
eval(parse(text=codeInput()), envir=shinyEnv)
})
}
shinyApp(ui, server)

Related

Is there a way to render plots from Data Explorer library on Shiny App

I'm trying to plot the outcomes of EDA on to Shiny App, I have been using DataExplorer library for the same and i'm able to perform operations on rmarkdown notebook. I was thinking to integrate the plots to shiny app using the below code but i'm running into errors,Can you please assist me in this regard and also suggest me if there is a way possible to achieve this.
UI part
library(shiny)
library(DataExplorer)
fluidRow(width=12,
column(12,plotOutput("struct"))
)
Server block
df<-read.csv("/path/to/csv/file.csv")
output$struct<-renderPlot({
req(df)
plot_str(df)
})
Thanks for the help in advance
DataExplorer::plot_str by default prints a networkD3::diagonalNetwork, however, it returns a list.
If you want to render the diagonalNetwork object in shiny you'll need to use networkD3::renderDiagonalNetwork. Please check the following:
library(shiny)
library(DataExplorer)
library(datasets)
library(networkD3)
# DF <- read.csv("/path/to/csv/file.csv")
DF <- mtcars
ui <- fluidPage(
fluidRow(column(12, diagonalNetworkOutput("struct")))
)
server <- function(input, output, session) {
output$struct <- renderDiagonalNetwork({
req(DF)
diagonalNetwork(plot_str(DF, print_network = FALSE))
})
}
shinyApp(ui, server)

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

How to display live poll results using R Shiny dashboard

I apologize for a very novice question.
I'm learning to use R shiny dashboard to display live infographics of polls performed through google forms.
This is the trial form: https://forms.gle/pixQ2pui5Qmgh9A4A
And this is the url that may be used to extract the .csv output of its responses:
https://docs.google.com/spreadsheets/d/1yS1l3Scvw98ueg5ZZe4021a3y5gMqe6FOP-ZrQIvHBo/export?format=csv&id=1yS1l3Scvw98ueg5ZZe4021a3y5gMqe6FOP-ZrQIvHBo&gid=1120079968
I understand that the reactiveFileReader() function should update the data continuously, but this does not seem to work, and the plot does not get updated unless the page is refreshed manually. How can the data be made to update itself continuously instead?
Thanks all!
library(shiny)
library(shinydashboard)
ui <- shinyUI(
dashboardPage(
dashboardHeader(title = "Data streaming"),
dashboardSidebar(
menuItem("Plot")
),
dashboardBody(
fluidRow(
box(plotOutput("histogram"))
)
)
)
)
server <-
shinyServer(function(input, output, session){
form.url = "https://docs.google.com/spreadsheets/d/1yS1l3Scvw98ueg5ZZe4021a3y5gMqe6FOP-ZrQIvHBo/export?format=csv&id=1yS1l3Scvw98ueg5ZZe4021a3y5gMqe6FOP-ZrQIvHBo&gid=1120079968"
dat <- reactiveFileReader(1000,
session,
filePath=form.url,
readFunc = function(filePath) {
read.csv(url(filePath))
})
output$histogram <- renderPlot({
hist(dat()$N, cex.main="", xlab="Poll", breaks=5)
})
})
shinyApp(ui, server)
formr is a free solution to live poll visualisation with R. They can host your polls but you can also host your own.

Shiny print reactive df to console (glimpse(myreactive_df) for debugging purposes?

I'm trying to debug my Shiny app and would like to view a reactive dataframe with e.g. glimpse(df).
Initially I tried to create a breakpoint and then view the environment by my reactive df is a value not an object when used inside server.r. I also tried browser() but was not sure what it would do.
I did some searching on SO and tried various things using sink(), renderPrint() but had no success.
How can I print the contents of glimpse(some_reactive_df()) to the console when I run my app?
Calling print() from within the reactive({}) expression will do that.
library(shiny)
library(dplyr)
shinyApp(
ui <- fluidPage(
selectizeInput("cyl_select", "Choose ya mtcars$cyl ", choices = unique(mtcars$cyl)),
tableOutput("checker") # a needed output in ui.R, doesn't have to be table
),
server <- function(input, output) {
d <- reactive({
d <- dplyr::filter(mtcars, cyl %in% input$cyl_select)
print(glimpse(d)) # print from within
return(d)
})
output$checker <- renderTable({
glimpse(d()) # something that relies on the reactive, same thing here for simplicty
})
})
Assuming you provide Shiny a reason to run (and re-run) your reactive of interest, by having it be involved with a rendering in server() and linked output in ui(). This is usually the case for my debugging scenarios but it won't work unless the reactive is being used elsewhere in app.R.

display of 'sync' (mapview) in shiny

I’m trying to have two spatial plots side-by-side in shiny, and I was suggested a powerful function, sync of mapview. After figuring how to display mapview object in shiny, I tried to integrate sync in 'shiny', but got the following error: Error in slot(x, "map") : no slot of name "map" for this object of class "shiny.tag.list" . Does it mean sync does not have map object, therefore, it is not possible to integrate sync or latticeView with shiny? If so, I guess there should be workaround solutions and my ears are all open. This is a nice feature to have access from Shiny and allows to do some interesting things. Greatly appreciate any suggestions. Here is the sample reproducible code:
library(shiny)
library(mapview)
ui <- fluidPage(
mapviewOutput("samplemap"),
p()
)
server <- function(input, output, session) {
output$samplemap <- renderMapview({
m1 <- mapview(gadmCHE,zcol="ID_1")
m2 <- mapview(gadmCHE,zcol="OBJECTID")
sync(m1,m2)
})
}
shinyApp(ui, server)
We have discussed making the return value from sync an htmlwidget. Currently, sync returns a htmltools::tagList of the leaflet maps. Inserting tags into shiny will be a little different than inserting mapview. I'll try to explain in code below.
library(mapview)
m1 <- mapview(gadmCHE,zcol="ID_1")
s1 <- sync(m1,m1)
library(shiny)
# if not dynamically adding maps
# we can just insert as ui
shinyApp(
ui = s1,
server = function(input,output){}
)
# if there is a need to create the maps after UI
# then we will need to handle differently
# since sync gives us tags instead of an htmlwidget
shinyApp(
ui = uiOutput("syncmap"),
server = function(input,output){
output$syncmap = renderUI({
s1
})
}
)

Resources