Using Shiny to create a comma delimited string list - shiny

How do you handle it in Shiny when you need to append a value to an already existing output?
To simplify my problem:
I want to create a comma separated code list in a single variable ie:
02,04,05,11,31
and display the list as I go creating it. I validate the codes as I go, that is not the problem.
I currently have a text input widget to enter my codes.
I want to append to the list the value in the text input field, every time I press an action button.
Are there any examples of how to do this?
Shiny does not like it when I try to use an output object and append something to it.

You can use Paste to do that. Im sure there are many other ways to do it, look into this example here reactivePoll and reactiveFileReader in the gallery section. Below are a sample code where I simply print out the Sys.time() and append it to the last entry.
Here are two examples:
Example 1 without the button
library(shiny)
runApp(list(ui = fluidRow(wellPanel(verbatimTextOutput("my_text"))),
server = function(input, output, session) {
autoInvalidate <- reactiveTimer(1000,session)
my_file <- as.character(Sys.time())
output$my_text <- renderText({
autoInvalidate()
my_file <<- paste(my_file,as.character(Sys.time()), sep=",")
})
})
)
Example 2 with the ActionButton
library(shiny)
runApp(list(ui = fluidRow(actionButton("push","Append"),wellPanel(verbatimTextOutput("my_text"))),
server = function(input, output, session) {
my_file <- as.character(Sys.time())
output$my_text <- renderText({
if(input$push==0)
{
return(my_file)
}
isolate({
input$push
my_file <<- paste(my_file,as.character(Sys.time()), sep=",")
})
})
})
)

Related

isolate input in a module shiny when using insert ui

I'd like to create several ui which use an input parameter. The problem is that the new UI created are still reacting to the input even when I put an isolate()
The right behaviour would give a custom UI created and isolated from the new inputs coming from the selectInput()
For instance I'd like a first UI with the year 2019 selected and second UI with the year 2020.
Here we can see that adding 2020 will change in each UI which is wrong.
library(shiny)
customplotUI <- function(id){
ns <- NS(id)
fluidPage(
sidebarPanel(id=ns("sidebarpanel"),
actionButton(ns("add"),label = "Add"),
selectInput(inputId=ns("years"),label="Year :", choices = c(2019,2020),selected = 2019, multiple = TRUE)),
mainPanel(div(id=ns("placeholder"))
)
)
}
customplot <- function(input,output,session){
ns <- session$ns
output$res <- renderPrint({
data <- data.frame(year=c(2019,2020),value=c("mtcars2019","mtcars2020"))
data[data$year %in% input$years,]})
ctn <- reactiveVal(0)
Id <- reactive({
function(id){
paste0(id, ctn())
}
})
IdNS <- reactive({
function(id){
ns(paste0(id, ctn()))
}
})
observeEvent(input$add, {
ctn(ctn() + 1)
print(Id()('div'))
insertUI(
selector = paste0('#', ns('placeholder')),
ui = div(
id = Id()('div'),
verbatimTextOutput(IdNS()('chart'))
)
)
id <- Id()('chart')
output[[id]] <- renderPrint({
data <- data.frame(year=c(2019,2020),value=c("mtcars2019","mtcars2020"))
#data[data$year %in% isolate(input$years),]
data[data$year %in% input$years,]
})
})
}
ui <- fluidPage(
customplotUI(id="customplot")
)
server <- function(input, output, session){
callModule(customplot,id="customplot",session=session)
}
shinyApp(ui, server)
Perhaps I'm misunderstanding what you're trying to accomplish, but when I run the code, using the commented line with isolate seems to work as intended.
I'm guessing that in creating the minimal reprex (thank you for doing this btw!), you might have gone a little too minimal and removed another reactive that updates data. If you are trying to have the individual UI elements update based on some other input but keep the same filtering scheme, you need to capture the current value of input$years outside of the renderPrint statement.
Here you can see the subset of rows is unchanged, but the last column updates based on input box:
...
id <- Id()('chart')
targetYears <- input$years
output[[id]] <- renderPrint({
data <- data.frame(year=c(2019,2020),
value=c("mtcars2019","mtcars2020"),
yrInput = paste(input$years, collapse =" "))
data[data$year %in% targetYears, ]
...
isolate only prevents a change in the reactive from triggering an update. If the update is triggered by something else, the current/updated value of the reactive is still used. Through the wonders of R's scoping rules, by capturing the value of input$years in non-reactive variable, targetYears, outside of the renderPrint call and then using that in the renderPrint expression it will always use the the value of the input when output[[id]] was created. The isolate is not needed as you are using observeEvent which will prevent the observer from executing when you change the input.

How to format R Shiny numericInput?

I have a Shiny app with numerous numericInput fields. I would like a way to format the numericInput fields with commas separating every 10^3. For example, I want 5,000,000 instead of 5000000.
I can do this in R with the format and prettyNum functions. But I don't have a way to do this in Shiny.
This would be very helpful for the UI because it would work with percents, money, etc. Does anyone have any idea how to incorporate this into the numericInput field?
Thanks!
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
mainPanel(
numericInput("formatNumber",
"Number should be formatted, e.g."5,000,000",
value = 1000),
p(format(5000000.10, big.mark=",", big.interval=3L,
digits=0, scientific=F))
)
)
server <- function(input, output) {
}
shinyApp(ui = ui, server = server)
The shinyWidgets package has a great new function (added as of version 0.5.4, also a disclaimer, I added it via a pull request), autonumericInput that will allow you to do just this. It is based on the javascript library autonumeric. There are a lot of options to the function, but the documentation is extensive and for simple uses most can be ignored.
What you are trying to do can be accomplished as follows:
library(shiny)
library(shinyWidgets)
ui <- fluidPage(
h1("Autonumeric Input Example"),
shinyWidgets::autonumericInput(
inputId = "num",
label = "Enter a large number:",
value = 1000000,
currencySymbolPlacement = "p",
decimalPlaces = 2,
digitGroupSeparator = ",",
decimalCharacter = "."
),
verbatimTextOutput("res1")
)
server <- function(input, output) {
output$res1 <- renderText(input$num)
}
shinyApp(ui = ui, server = server)
This is especially nice because it provides as-you-type formatting, so that the user can easily know how big the number is as they put it in. I know from experience that it is a real pain to try to put large numbers into the base shiny numericInput, trying to count digits in a small little box and figure out how many zeros there are. The goal of this function is to make formatting numeric inputs much easier.
Hopefully this is useful!
I could not find anything that would help with numericInput(), but here's what works with textInput() instead.
library(shiny)
if(interactive()){
shinyApp(
ui <- fluidPage(
mainPanel(
textInput("formatNumber1", "Number should be formatted, e.g.5,000,000", value = 1000),
textInput("formatNumber2", "Number should be formatted, e.g.5,000,000", value = 1000)
)
),
server <- function(input, output, session) {
observe({
updateTextInput(session, "formatNumber1", "Number should be formatted, e.g.5,000,000",
value = prettyNum(input$formatNumber1, big.mark=",", scientific=FALSE))
updateTextInput(session, "formatNumber2", "Number should be formatted, e.g.5,000,000",
value = prettyNum(input$formatNumber2, big.mark=",", scientific=FALSE))
})
}
)
}
That is the only method I found, however if you're too slow or add a digit after the commas have been added, the number is not displayed properly (e.g., 3,000 becomes 3,0,000 if you add a 0 at the end of the string). To correct that, I've changed the updateTextInput() function as below:
updateTextInput(
session,
"formatNumber1",
"Number should be formatted, e.g.5,000,000",
value = prettyNum(
gsub(",", "", input$formatNumber1),
big.mark=",", scientific=FALSE
)
)
In effect gsub() function is used to reset the input to a number every time the input is amended, otherwise the prettyNum() function is only using the digits after the comma and ignoring all digits on the left of the last comma.
If you've got multiple inputs to reformat, then create a function as follows (NB: I've also added req(input[[x]]) to avoid NA appearing when the input is blank):
updatetoprettynb <- function(x) {
req(input[[x]])
updateTextInput(
session,
x,
value = prettyNum(
gsub(",", "", input[[x]]),
big.mark = ",",
scientific = FALSE
)
)
}
You still have to use the function in a similar fashion but don't forget to use "":
observe({
updatetoprettynb("formatNumber1")
})

How do I write to a bigquery table from shiny?

I'm trying to write a shiny app that takes a file as an input and uploads the data in that file to a bigquery table where some other stuff will go on. Everything appears to be working fine in terms of getting the data into my app, but when I try to upload the data to bigquery, nothing happens. No error messages, just nothing.
I can run the code on its own and it executes just fine. I'm having a little trouble figuring out how to create a reproducible example because you can't write to a public dataset, but I've included my code below.
Additional info:
working directory contains my .httr-oauth file
data is visible in my shiny app
Please let me know if there's something I can add to make this question easier to answer. Thanks.
############# UI ############
#
library(shiny)
shinyUI(fluidPage(
# Application title
titlePanel("Upload"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
fileInput('list', 'Choose file to upload',
accept = c(
'text/csv',
'text/comma-separated-values',
'.csv'
)),
tags$hr(),
textInput('sql', 'Or give a query to get the customer_ids you want'),
tags$hr(),
actionButton('go', 'Go')
),
# Show a plot of the generated distribution
mainPanel(
tableOutput('log')
)
)
))
############# server ##############
### setting up the environment
library(shiny)
library(data.table)
library(bigrquery)
### setting up the constants
project <- 'xxxxxxx'
dest_dataset <- 'temp'
dest_table <- 'custs_hash'
cd <- 'CREATE_IF_NEEDED'
wd <- 'WRITE_TRUNCATE'
options(shiny.maxRequestSize = 100*1024^2)
shinyServer(function(input, output) {
logs <- eventReactive(input$go, {
inFile <- input$list
dat <- fread(inFile$datapath)
dat <- head(dat)
return(list(dat = dat))
})
upload <- eventReactive(input$go, {
data <- dat()$dat
ins <- insert_upload_job(project, dataset = dest_dataset, table = dest_table, values = data,
create_disposition = cd, write_disposition = wd)
return(list(ins = ins))
})
output$log <- renderTable(logs()$dat)
})
An eventReactive returns a reactive expression object. Like other reactive objects, you need to expressly call it like a function. Otherwise it won't run by itself.
So in your case, you have upload <- eventReactive(...), then you need to call it using upload().

Click on marker to open plot / data table

I'm working on leaflet with shiny. The tools is basic, i have a map with some markers (coming from a table with LONG and LAT).
What I want to do is to open a table or a graph when i click on the marker.
Is there a simple way to do it?
Do you have a really simple example: you have a maker on a map, you click on the marker, and there is a plot or a table or jpeg that s opening?
Here is another example, taken from here and a little bit adapted. When you click on a marker, the table below will change accordingly.
Apart from that, a good resource is this manual here:
https://rstudio.github.io/leaflet/shiny.html
library(leaflet)
library(shiny)
myData <- data.frame(
lat = c(54.406486, 53.406486),
lng = c(-2.925284, -1.925284),
id = c(1,2)
)
ui <- fluidPage(
leafletOutput("map"),
p(),
tableOutput("myTable")
)
server <- shinyServer(function(input, output) {
data <- reactiveValues(clickedMarker=NULL)
# produce the basic leaflet map with single marker
output$map <- renderLeaflet(
leaflet() %>%
addProviderTiles("CartoDB.Positron") %>%
addCircleMarkers(lat = myData$lat, lng = myData$lng, layerId = myData$id)
)
# observe the marker click info and print to console when it is changed.
observeEvent(input$map_marker_click,{
print("observed map_marker_click")
data$clickedMarker <- input$map_marker_click
print(data$clickedMarker)
output$myTable <- renderTable({
return(
subset(myData,id == data$clickedMarker$id)
)
})
})
})
shinyApp(ui, server)
There is a leaflet example file here:
https://github.com/rstudio/shiny-examples/blob/ca20e6b3a6be9d5e75cfb2fcba12dd02384d49e3/063-superzip-example/server.R
# When map is clicked, show a popup with city info
observe({
leafletProxy("map") %>% clearPopups()
event <- input$map_shape_click
if (is.null(event))
return()
isolate({
showZipcodePopup(event$id, event$lat, event$lng)
})
})
Online demo (see what happens when you click on a bubble):
http://shiny.rstudio.com/gallery/superzip-example.html
On the client side, whenever a click on a marker takes place, JavaScript takes this event and communicates with the Shiny server-side which can handle it as input$map_shape_click.

R Shiny - How to use local csv file and custom function

I started to learn R Shiny recently and I tried to build an shiny app and failed. My problem is: I try to use local csv file on my computer and run simple look up using the custom function, like type in the student's name and return his/her student ID. The following is my codes:
ui.R
library(shiny)
shinyUI(fluidPage(
# application title
titlePanel("FGMC Search Engine"),
sidebarLayout(
sidebarPanel(
textInput("text",label = h3("Correspondent Search"), value = "Enter Here...")
),
mainPanel(
textOutput("text1")
)
)
))
and then
server.R
library(shiny)
# define function for inputing the corr array & input and return channel source
corr_search <- function(corr,input_name){
# lowercase of input names
name_lower = tolower(input_name)
# lowercase of corr names
corr_lowercase = substr(tolower(corr$TPO.Company.Name),1,nchar(test_lowercase))
# look up the corresponding channel source accounding to matched corr indexing
result = corr$Channel.Source[match(test_lowercase, corr_lowercase)]
# check if search result is Null
if (is.na(result)){
result = "Wrong Input! Please Search Again"
}
# return search result
return(result)
}
shinyServer(
function(input, output) {
textInput <- reactive({
corr = read.csv("C:\\Users\\carl.qin\\Desktop\\Projects\\Modelling & Analytics\\R Modelling\\App-2\\fgmc_correspondent.csv",header=TRUE)
input_name = input$text
If(is.na(input_name)){
input_name = "nothing"
}
})
result = corr_search(corr,input_name)
output$text1 <- renderPrint({
result
})
}
)
I keep getting the error: Object not found. It would be great if someone could help solve this problem.
Thank you!
In the function corr_search you use a variable test_lowercase which is nowhere defined (doesn't exist and hence an error). In reactive you use if-statement but you have a typo. (Instead of If you should have if). You should also move result = corr_search(corr, input_name) to the reactive expression textInput (where you have defined input_name) and return a value which you finally pass to renderPrint via textInput()