Ensure Shiny graphs only load once in shinyMobile f7Tab - r

I'm using the awesome shinyMobile package to create a mobile app. It has 4 tabs (f7Tab()), including a dashboard tab where I load all my charts. The idea is to load the charts when the user shifts to the tab so they don't slow down the initial app load, but also ONLY LOAD THE GRAPHS ONCE unless an input is triggered.
Here is a sample of my server code (there are multiple graphs like this):
# Reactively render timeseries graph
observe({
# Check which tab is opened (tab label should be 'Dashboard')
if (reactiveVal(input$mainTabs)() == 'Dashboard') {
output$tseriesGraph <- renderPlotly({
tseries_basic(input$tseriesVar)
})
}
})
And the corresponding UI code:
# Timeseries plots
f7AccordionItem(
id='tseriesAccord',
title='Timeseries of individual variables',
# Choose var to plot
f7Select(
'tseriesVar',
'Choose variable to plot:',
selected=chosen_vars[1],
choices=chosen_vars
),
br(),
withSpinner(plotlyOutput("tseriesGraph",
height='350px')
)
)
As it stands, every time the user switches back to the Dashboard tab, all of the graphs get reloaded by the server, even if they were previously loaded. How can I prevent Shiny from reloading these graphs unnecessarily?
(Also, out of curiosity, is there any way to get the graphs to load when the f7AccordionItem with id=tseriesAccord is loaded? Doesn't seem to be an id argument for this function.)
Thanks very much!

I ended up finding a workaround: use f7Accordion id to determine whether the graph should load or not. So don't even need to use the code to check whether my Dashboard was open. I needed to use the 'state' object, which shows as TRUE or FALSE depending on whether the f7AccordionItem under f7Accordion is open.
Example:
# Server
observe(if (input$spagBasicAccord$state) {
output$spagBasicGraph <- renderPlotly({
spag_basic(input$spagBasicVar)
})
})
# UI
# Basic spaghetti graph
f7Accordion(
id='spagBasicAccord',
f7AccordionItem(
title='Basic spaghetti graph',
f7Select(
'spagBasicVar',
'Choose variable to plot:',
selected=chosen_vars[1],
choices=chosen_vars
),
br(),
withSpinner(plotlyOutput("spagBasicGraph",
height='350px')
)
)
)

Related

R Shiny Plots changing without update button being pressed

I'm attempting to design a Shiny App where the user changes several inputs, then presses a "Plot" button to generate new visualizations. Currently, I have the a data object being generated in an eventReactive tied to the "Plot" button, with that data then getting fed into the renderPlot functions.
The framework seems to work, except that the plots will change whenever the inputs are changed. This often leads to errors in the plots, as different inputs load in different data. Pressing the "Plot" button after changing the inputs will cause the correct plots to render, but I'm trying to find a way to ensure that the plots don't change ever until that button is hit.
I believe the solution is a use of the "isolation" function, and I've tried that just about everywhere. However, none of these attempts have been successful. Below is a (simplified) setup of my code.
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
# several selectInput options
actionButton('plot', label = 'Plot')
)
mainPanel(
plotOutput('outputPlot', height = '3in'),
)
)
)
server <- function(input, output) {
plotData <- eventReactive(input$plot, {
# load various data and organize into a list
return(data.list)
})
outPutPlot <- renderPlot({
plot.data <- plotData()
# manipulate data based on the specific plot, then generate
return(plot)
)
}
You’re right that you’d need to isolate() all reactive dependencies other
than plotData(). Without having a complete runnable example, it’s not
possible to point out where this might have gone wrong. However, it may be
easier to wrap the renderPlot() call in bindEvent(), which will isolate
everything. You just pass the expressions you want to depend on in other
arguments.
So try something like this:
bindEvent(renderPlot({ ... }), plotData())

R Shiny : Refreshing plot when entering input OR pressing an action button

I'm fairly new to R Shiny but am stuck with the following problem for some time now and hope you can provide some help:
I try to refresh a plot in R shiny that should be refreshed if either a new input argument is entered OR an action button is pressed. This should be straightforward, but unfortunately I can't solve it, despite googling/reading instructions for some time. Any advice would be recommended. Any solutions on the web seem to put the whole renderplot function inside the observeEvent function, but I also need the renderplot in addition outside of it to account for the possibility of just entering inputs without pressing the action button.
I have no trouble creating a (render)plot that either exclusively is refreshed when entering a new input or exclusively refreshed when pressing a button.
However when doing both at the same I fail: I first tried to copy the renderplot function including the resulting output twice one time within an observeEvent function (to account for clicking the action button) and one time outside of an observeEvent (to account for only refreshing the inputs to the plot) but this leads only to a greyed out graph that refreshes after ~10 seconds delay when pressing the action button. I imagine adding the reactive click input generated from clicking the action button directly to the renderplot outside of observe event , but so far I couldn't get it to run. Any recommendations would be much appreciated.
Thank you in advance.
Like this?:
Edit: No need to pass the selectInput to the reactive Vals.. this does the same:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(inputId="select", label="title", choices=LETTERS[1:3], selected = "A"),
actionButton(inputId="btn", label="refresh")
),
mainPanel(
plotOutput("scatterPlot")
)
)
)
server <- function(input, output) {
plotSettings <- reactiveValues()
observeEvent(c(input$btn, input$select), {
plotSettings$values <- runif(100,1,100)
# plotSettings$title <- input$select
}, ignoreNULL = FALSE)
output$scatterPlot <- renderPlot({
plot(plotSettings$values, main=input$select)
})
}
shinyApp(ui = ui, server = server)

Allow user to load their data via CSV or use a sample dataset

I'm building a Shiny app that consists of
A fileInput for the user to upload a CSV of transactions
An actionButton that lets the user test the app with a pre-built dataset (i.e. without them having to load their own data).
A verbatimTextOutput that prints a preview of the dataset they're using and
Various plot and charts built using their selected dataset
If the user uploads a file, that dataset should become the "master" transactions dataset to feed the rest of the app. If they then click the "load sample data" button, that datset should turn into the "master" transactions dataset. (Extrapolate this idea to multiple alternations between them uploading data and clicking the button)
I can get this to work as follows:
# app.R
library(data.table)
library(shiny)
# UI
ui <- shinyUI(fluidPage(
fileInput(inputId='fi_file', label='Choose CSV File', accept=c('text/csv', 'text/comma-separated-values,text/plain', '.csv')),
actionButton(inputId="ab_loadSampleTransactions", label="Load Sample Transactions"),
verbatimTextOutput("vto_transactions")
))
# Server
server <- shinyServer(function(input, output) {
# When the user uploads a file, print it
observeEvent(input$fi_file, {
transactions <- read.csv(input$fi_file$datapath)
output$vto_transactions <- renderPrint(transactions)
})
# When the user clicks the button for sample transactions, print them
observeEvent(input$ab_loadSampleTransactions, {
transactions <- data.table(ID=c(1,2,3), Amount=c(100, 150, 125))
output$vto_transactions <- renderPrint(transactions)
})
# More logic involving the transactions dataset
# ...
})
# Run the application
shinyApp(ui = ui, server = server)
However, this is inefficient because it requires me to load the transactions dataset twice in order to display it and do future logic with it. I think I need to do something reactive here, but I can't figure out how since I have two separate methods for loading the data. Help?
Don't use global variables like the suggestion in the comment says.
Use reactiveValues. It sounds like you don't need this variable to be "global" in the sense that it needs to be shared with the UI and other files -- it just needs to be global within the server, correct? In that case, you can use reactiveValues and those variables can be accessed and set anywhere in your server

display different number of tabPanels in a navbarPage in Shiny, without renderUI or uiOutput

I am trying to display from 1 to 5 tabPanels in a navbarPage in Shiny.
I have 5 plots my code generates, but I'd like a user to be able to select how many they want to have access to -- to be displayed one plot in each tabPanel, naturally.
I've got an external configuration file (config.txt) that via source('config.txt'), I have access to a number_of_pages variable.
For example, number_of_tabPages <- 3
How would I set this up in UI.R?
The number of tabPanels can't be hardcoded at all in the UI file, since it depends on a value that is specified by a user, not using a control.
I've searched around and found that most of the approaches to this kind of thing
involve using uiOutput and renderUI functions, such as this similar problem, but I don't want any special control in the UI to do any selecting.
This is where things get tricky, when we are building the UI depending on values that may change. My brain is trying to wrap itself around the best method for doing this type of thing -- I feel like it isn't exactly in line with how Shiny wants to communicate with itself using a UI <--> server environment.
Any advice is greatly appreciated.
My UI.R is easy to create when it isn't dynamic:
fluidRow(
column(12,
"",
navbarPage("",tabPanel("First Tab",
plotOutput("plot1")),
tabPanel("Second Tab",
plotOutput("plot2")),
tabPanel("Third Tab",
plotOutput("plot3")),
tabPanel("Fourth Tab",
plotOutput("plot4")),
tabPanel("Fifth Tab",
plotOutput("plot5"))
)
)
)
)
Thanks!
If you don't need the user to change the number of tabPanel interactively, but just load varying numbers of them when the app is started you can use the do.call function in the navBarPage:
library(dplyr)
library(shiny)
library(ggvis)
#number of tabs needed
number_of_tabPages <- 10
#make a list of all the arguments you want to pass to the navbarPage function
tabs<-list()
#first element will be the title, empty in your example
tabs[[1]]=""
#add all the tabPanels to the list
for (i in 2:(number_of_tabPages+1)){
tabs[[i]]=tabPanel(paste0("Tab",i-1),plotOutput(paste0("plot",i-1)))
}
#do.call will call the navbarPage function with the arguments in the tabs list
shinyUI(fluidRow(
column(12,
"",
do.call(navbarPage,tabs)
)
)
)

Shiny - Taking Text input into auxiliary function

I am new to Shiny and trying to build a more accessible input and output for a function I built. I am giving this to people that don't run R so trying to build something that runs my functions in the background and then spits out the answer.
I am having some trouble getting everything in the way that I want it unfortunately and dealing with a bunch of errors. However, here is my more pointed question:
The actual function that I want to run takes a name (in quotations as "Last,First") and a number.
PredH("Last,First",650)
So I want a shiny application that takes a name input and a number input that then runs this program and then spits back out a data table with my answer. So a couple of questions.
How do I get it in the right form to input into my equation on the server side script, do I need to return it in the function so it can be accessed using a function$table type access? (Right now I am just printing using cat() function in the console for the function but know that may not be usable for this type of application.
I want to return a dataframe that can be gotten at PredH14$table. How do I go about building that shiny?
Here is my code so far:
UI:
library(shiny)
shinyUI(pageWithSidebar(
# Application title
headerPanel("Miles Per Gallon"),
# Sidebar with controls to select the variable to plot against mpg
# and to specify whether outliers should be included
sidebarPanel(
textInput("playername", "Player Name (Last,First):", "Patch,Trevor"),
radioButtons("type", "Type:",
list("Pitcher" = "P",
"Hitter" = "H"
)),
numericInput("PAIP", "PA/IP:", 550),
submitButton("Run Comparables")
),
mainPanel(
textOutput("name")
)
Server:
library(shiny)
shinyServer(function(input, output) {
sliderValues <- reactive({
data.frame(
Name = c("name", "PA"),
Value = c(as.character(playername),
PAIP),
stringsAsFactors=FALSE)
})
name=input[1,2]
PAIP=input[2,2]
testing <- function(name,PAIP){
a=paste(name,PAIP)
return(a) }
output$name=renderText(testing$a)
})
I am not quite sure I understood your question 100% but I clearly see you are wondering how to pass the input of the UI into the server and maybe, the other way back.
In your server code, clearly you are not getting any input from the UI. Basically you have created three input variables in your ui.R:
1. input$playername
2. input$type
3. input$PAIP
And one output:
1. output$name
Just let you know, the function sliderValues <- reactive(..) is called every time there is any input from the input... like people click the dropdown list or people modifies words in the text box.
You can even get started without the submit button just to get started. But the existence of the submit button actually makes everything easier. Create a submit button for an input form. Forms that include a submit button do not automatically update their outputs when inputs change, rather they wait until the user explicitly clicks the submit button.
So you can put your code in a way similar like this:
# server.R
library(shiny)
shinyServer(function(input, output) {
sliderValues <- reactive({
result <- ... input$playername ... input$type ... input$PAIP
return(result)
})
output$name <- renderPlot/renderText (... sliderValues...)
})
# ui.R
library(shiny)
shinyUI(pageWithSidebar(
headerPanel("Miles Per Gallon"),
sidebarPanel(
textInput("playername" ... ),
radioButtons("type" ... ),
numericInput("PAIP" ... ),
submitButton("...")
),
mainPanel(
textOutput/plotOutput...("name")
)
))
In the end, check out the shiny example that might be what you want.
library(shiny)
runExample('07_widgets')

Resources