I am trying to implement Stephane Laurent's solution to requiring a minimum number of selections for a picker input in a Shiny Markdown document but cannot figure out how to run the java script component. Can anyone tell me where I place the js and tags$head... objects to make this run correctly?
Solution Using a Traditional Shiny App
library(shiny)
library(shinyWidgets)
js <- "
$(document).ready(function(){
$('#somevalue').on('show.bs.select', function(){
$('a[role=option]').on('click', function(e){
var selections = $('#somevalue').val();
if(selections.length === 1 && $(this).hasClass('selected')){
e.stopImmediatePropagation();
};
});
}).on('hide.bs.select', function(){
$('a[role=option]').off('click');
});
});"
ui <- fluidPage(
tags$head(tags$script(HTML(js))),
pickerInput(
inputId = "somevalue",
label = "A label",
choices = c("a", "b"),
selected = "a",
multiple = TRUE
),
verbatimTextOutput("value")
)
server <- function(input, output) {
output$value <- renderPrint(input$somevalue)
}
shinyApp(ui, server)
Shiny Markdown Document Wrapper Doesn't Work
---
runtime: shiny
output:
html_document
---
```{r echo = F, message = F, warning = F, error = F}
library(shiny)
library(shinyWidgets)
js <- "
$(document).ready(function(){
$('#somevalue').on('show.bs.select', function(){
$('a[role=option]').on('click', function(e){
var selections = $('#somevalue').val();
if(selections.length === 1 && $(this).hasClass('selected')){
e.stopImmediatePropagation();
};
});
}).on('hide.bs.select', function(){
$('a[role=option]').off('click');
});
});"
tags$head(tags$script(HTML(js)))
pickerInput(
inputId = "somevalue",
label = "A label",
choices = c("a", "b"),
selected = "a",
multiple = TRUE
)
```
I have not tried, but I think you can include a JavaScript chunk:
```{js}
$(document).ready(function(){
$('#somevalue').on('show.bs.select', function(){
$('a[role=option]').on('click', function(e){
var selections = $('#somevalue').val();
if(selections.length === 1 && $(this).hasClass('selected')){
e.stopImmediatePropagation();
};
});
}).on('hide.bs.select', function(){
$('a[role=option]').off('click');
});
});
```
Related
I am trying to include LateX formulas inside a table and I am using the MathJack library to do so. Everthing is working smoothly outside a modalDialog, but when the table is produced within the modalDialog, it does not show as expected. I guess it has do to with what is written in the help page "It only needs to be called once in an app unless the content is rendered after the page is loaded, e.g. via renderUI(), in which case we have to call it explicitly every time we write math expressions to the output.". But I can't figure out how to solve the issue.
Here is a repex :
library(shiny)
ui <- shinyUI(
fluidPage(
withMathJax(),
actionButton("open", "Open")))
server <- function(input, output, session){
output$mytable <- renderTable({
df <- data.frame(A = c(HTML("$$\\alpha+\\beta$$"), "$$\\alpha+\\gamma$$", "$$\\alpha+\\lambda$$"),B = c(111111, 3333333, 3123.233))
df
}, sanitize.text.function = function(x) x)
observeEvent(input$open, {
showModal(modalDialog(
withMathJax(),
h2("$$\\mbox{My Math example }\\sqrt{2}$$"),
tableOutput('mytable')))
})
}
shinyApp(ui = ui, server = server)
Oddly, that works like this:
observeEvent(input$open, {
showModal(withMathJax(modalDialog(
h2("$$\\mbox{My Math example }\\sqrt{2}$$"),
withMathJax(tableOutput('mytable')))))
})
EDIT
Since there are some problems with this solution, here is a solution using KaTeX instead of MathJax:
library(shiny)
js <- "
$(document).on('shiny:value', function(event) {
if(event.name === 'mytable'){
// h2 element
var $h2 = $('#title');
var title = $h2.html();
var matches_title = title.match(/(%%+[^%]+%%)/g);
var i, code;
for(i=0; i<matches_title.length; i++){
code = matches_title[i].slice(2,-2);
title = title.replace(matches_title[i], katex.renderToString(code));
}
$h2.html(title);
$h2.css('visibility', 'visible');
// table:
var matches = event.value.match(/(%%+[^%]+%%)/g);
var newvalue = event.value;
for(i=0; i<matches.length; i++){
code = matches[i].slice(2,-2);
newvalue = newvalue.replace(matches[i], katex.renderToString(code));
}
event.value = newvalue;
}
})
"
css <- "#mytable td:nth-child(3) {display: none;}"
ui <- fluidPage(
tags$head(
tags$link(rel="stylesheet", href="https://cdn.jsdelivr.net/npm/katex#0.15.2/dist/katex.min.css", integrity="sha384-MlJdn/WNKDGXveldHDdyRP1R4CTHr3FeuDNfhsLPYrq2t0UBkUdK2jyTnXPEK1NQ", crossorigin="anonymous"),
tags$script(defer="", src="https://cdn.jsdelivr.net/npm/katex#0.15.2/dist/katex.min.js", integrity="sha384-VQ8d8WVFw0yHhCk5E8I86oOhv48xLpnDZx5T9GogA/Y84DcCKWXDmSDfn13bzFZY", crossorigin="anonymous"),
tags$script(HTML(js)),
tags$style(HTML(css))
),
titlePanel("Hello Shiny!"),
br(),
actionButton("open", "Open")
)
server <- function(input, output, session){
output$mytable <- renderTable({
data.frame(
A = c("%%\\alpha+\\beta%%", "%%\\alpha+\\gamma%%", "%%\\alpha+\\lambda%%"),
B = c(111111, 3333333, 3123.233),
` ` = rep(input$open, 3),
check.names = FALSE
)
}, sanitize.text.function = function(x) x)
observeEvent(input$open, {
showModal(modalDialog(
h2(
id = "title",
style = "visibility: hidden;",
"%%\\boxed{Math}\\sqrt{2}%%"
),
tableOutput("mytable")
))
})
}
shinyApp(ui, server)
Note that I include a reactive column in the dataframe:
` ` = rep(input$open, 3)
That's because the KaTeX rendering works only one time if I don't do that. Then I hide this column with some CSS.
I am trying to start a reactivePoll only after selecting a parent directory using shinyFiles. I want the function to check based on the selection. I keep getting checkFunc errors about argument is of length 0. I looked this up and typically happens when the vector is logical(0). I got this example from reactivePoll example. Please provide some help, thanks.
Warning: Error in if: argument is of length zero
reactivePoll
observeEventHandler
shiny::runApp
library(shiny)
library(shinyFiles)
home_dir <- "/top/directory"
server <- function(input, output, session) {
pdf_file <- 'full_report.pdf'
status <- reactiveValues(text = 'Idle')
shinyDirChoose(
input,
"dir",
roots = c(home = home_dir)
)
dir <- reactive(
basename(parseDirPath(c(home=home_dir), input$dir))
)
output$dirpath_dply <- renderText({
dir()
})
observeEvent(input$dir, {
session$sendCustomMessage('disableButton', 'dir')
status$text <- 'Running...'
# mimic httr post
Sys.sleep(5)
message(dir())
req(dir()) # seems to prevent error but just hangs?
omiqPDF_file <- reactivePoll(2000, session,
checkFunc = function() {
if (file.exists(file.path(home_dir, dir(), pdf_file)))
file.info(file.path(home_dir, dir(), pdf_file))$mtime
else
''
},
valueFunc = function() {
if ( length(list.files(path = paste0(home_dir, '/', input$dir),
pattern = pdf_file,
recursive=TRUE) ) > 0 )
file.info(file.path(home_dir, dir(), pdf_file))$size > 5000
else
''
}
)
observe({
if (omiqPDF_file() == TRUE) {
session$sendCustomMessage('enableButton', 'dir')
status$text <- paste0('Completed for ', dir())
}
})
})
output$uiStatus <- renderUI(
h4(paste0('STATUS: ', status$text), style="color:red;")
)
}
ui <- fluidPage(
singleton(tags$head(HTML('
<script type="text/javascript">
$(document).ready(function() {
// Enable button
Shiny.addCustomMessageHandler("enableButton", function(id) {
$("#" + id).removeAttr("disabled");
});
// Disable button
Shiny.addCustomMessageHandler("disableButton", function(id) {
$("#" + id).attr("disabled", "true");
});
})
</script>
')
)),
navbarPage(
"main",
tabPanel("navbar 1",
sidebarPanel(
tags$h2("HEADER"),
shinyDirButton("dir", "Input Directory", ""),
br()
),
mainPanel(
h1("HEADER MAIN"),
h4("DIRPATH OUTPUT"),
verbatimTextOutput("dirpath_dply"),
uiOutput('uiStatus')
)
),
tabPanel("navbar 2", "blank"),
tabPanel("navbar 3", "blank")
)
)
shinyApp(ui = ui, server = server)
I want to change the placeholder in fileInput after a file has been uploaded, i.e. customize the file name written.
I found how to customize the progress bar label, so I'm guessing the code should be quite similar. This is what I tried so far:
library(shiny)
library(shinyjs)
jscode_upload_msg <- " Shiny.addCustomMessageHandler('upload_msg', function(msg) {
var target = $('#fileUpload_progress').children()[0];
target.innerHTML = msg;
}); "
jscode_upload_txt <- " Shiny.addCustomMessageHandler('upload_txt', function(txt) {
var target = $('#fileUpload_header').children()[1].children()[0];
target.innerHTML = txt;
}); "
ui <- fluidPage(
useShinyjs(),
tags$script(jscode_upload_msg),
tags$script(jscode_upload_txt),
fileInput("fileUpload", "File to upload")
)
server <- function(input, output, session ) {
observe({
req(input$fileUpload)
session$sendCustomMessage("upload_msg", "YOUR TEXT")
session$sendCustomMessage("upload_txt", "SOME OTHER TEXT")
})
}
shinyApp(ui = ui, server = server)
From Shiny customise fileInput, it seems that the input field is in the second position. However, I am not sure how to write the jscode. Any advice?
library(shiny)
jscode_upload_msg <- " Shiny.addCustomMessageHandler('upload_msg', function(msg) {
var target = $('#fileUpload_progress').children()[0];
target.innerHTML = msg;
}); "
jscode_upload_txt <- " Shiny.addCustomMessageHandler('upload_txt', function(txt) {
var target = $('#fileUpload').parent().parent().parent().find('input[type=text]');
target.val(txt);
}); "
ui <- fluidPage(
tags$script(HTML(jscode_upload_msg)),
tags$script(HTML(jscode_upload_txt)),
fileInput("fileUpload", "File to upload")
)
server <- function(input, output, session ) {
observeEvent(input$fileUpload, {
session$sendCustomMessage("upload_msg", "YOUR TEXT")
session$sendCustomMessage("upload_txt", "SOME OTHER TEXT")
})
}
shinyApp(ui = ui, server = server)
I have about 20 thousand images in a datatable in a shiny application. I want to delete images I dont like and then download the resulting datatable (just with pathnames to the images I want).
I have managed to see a dataframe with each row showing me an image. I can delete rows I dont like. I'd like now to be able to download the dataframe (just with the paths of the files rather than the actual images) into a csv. I don't seem to be able to do this using the downloadHandler. What am I missing?
Here is my code:
server.R
library(shiny)
library(shinydashboard)
library(data.table)
library(DT)
server<-shinyServer(function(input, output) {
vals<-reactiveValues()
vals$Data<-data.table(
df
)
output$MainBody<-renderUI({
fluidPage(
box(width=12,
hr(),
column(12,dataTableOutput("Main_table")),
tags$script(HTML('$(document).on("click", "input", function () {
var checkboxes = document.getElementsByName("row_selected");
var checkboxesChecked = [];
for (var i=0; i<checkboxes.length; i++) {
if (checkboxes[i].checked) {
checkboxesChecked.push(checkboxes[i].value);
}
}
Shiny.onInputChange("checked_rows",checkboxesChecked);
})')),
tags$script("$(document).on('click', '#Main_table button', function () {
Shiny.onInputChange('lastClickId',this.id);
Shiny.onInputChange('lastClick', Math.random())
});")
)
)
})
output$downloadData <- downloadHandler(
filename = function() {
paste(input$Main_table, ".csv", sep = "")
},
content = function(file) {
write.csv(vals$Data, file, row.names = FALSE)
}
)
output$Main_table<-renderDataTable({
DT=vals$Data
datatable(DT,
escape=F)}
)
})
ui.R
ui<-fluidPage(dashboardHeader(disable = T),
dashboardSidebar(disable = T),
downloadLink("downloadData", "Download"),
dashboardBody(uiOutput("MainBody")
)
)
# Run the application
shinyApp(ui = ui, server = server)
The problem is that df is a function from library(stats). Please see ?df. Please, never use df as a variable name - this only causes confusion; take DF instead. You wrapped this function into data.table() and passed it into a reactiveValue (see the print() I added).
This construct is treated as a list() by write.csv() and it doesn't know what do do with a list() causing the following error:
Error in write.table: unimplemented type 'list' in 'EncodeElement'
Accordingly you might want to fix your data.table initialization by e.g. passing NULL instead of df:
library(shiny)
library(shinydashboard)
library(data.table)
library(DT)
server <- shinyServer(function(input, output) {
vals <- reactiveValues(myTabData = data.table(NULL))
vals$Data <- data.table(df)
print(paste("myData:", isolate(vals$Data)))
output$MainBody <- renderUI({
fluidPage(box(
width = 12,
hr(),
column(12, dataTableOutput("Main_table")),
tags$script(
HTML(
'$(document).on("click", "input", function () {
var checkboxes = document.getElementsByName("row_selected");
var checkboxesChecked = [];
for (var i=0; i<checkboxes.length; i++) {
if (checkboxes[i].checked) {
checkboxesChecked.push(checkboxes[i].value);
}
}
Shiny.onInputChange("checked_rows",checkboxesChecked);})'
)
),
tags$script(
"$(document).on('click', '#Main_table button', function () {
Shiny.onInputChange('lastClickId',this.id);
Shiny.onInputChange('lastClick', Math.random())});"
)
))
})
output$downloadData <- downloadHandler(
filename = function() {
"Main_table.csv"
},
content = function(file) {
write.csv(vals$myTabData, file, row.names = FALSE)
# Warning: Error in write.table: unimplemented type 'list' in 'EncodeElement'
# write.csv(vals$Data, file, row.names = FALSE)
}
)
output$Main_table <- renderDataTable({
DT = vals$Data
datatable(DT, escape = FALSE)
})
})
ui <- fluidPage(
dashboardHeader(disable = T),
dashboardSidebar(disable = T),
downloadLink("downloadData", "Download"),
dashboardBody(uiOutput("MainBody"))
)
shinyApp(ui = ui, server = server)
Furthermore you should fix your filename (input$Main_table is not existing)
The slickR package has a focusOnSelect option - when clicking on an image in the carousel, it is highlighted. How can I access the selection event to use in R Shiny to trigger other actions? Specifically, I want to click on an image and have it update a textbox with the image name.
To use the example below, put 3 images (image1.jpg, image2.jpg, image3.jpg) in the same directory as the app.
library(shiny)
ui <- shiny::basicPage(
slickROutput("my_slick",width='100%',height='200px')
)
server <- function(input, output) {
output$my_slick <- renderSlickR({
my_images <- c("image1.jpg", "image2.jpg", "image3.jpg")
slickR(
my_images,
slideId = 'slick_images',
width='90%'
)
})
}
shinyApp(ui, server)
Is it what you want ?
library(shiny)
library(slickR)
my_images <- c("image1.png", "image2.png", "image3.png")
ui <- shiny::basicPage(
slickROutput("my_slick",width='100%',height='200px'),
tags$p(id="textbox"),
tags$script('var my_images = ["image1.png","image2.png","image3.png"];
$("#my_slick").on("click", function(e){
var slideClicked = $(this).find(".slick-active").attr("data-slick-index");
document.getElementById("textbox").innerHTML = "Selected image: " + my_images[slideClicked];
});')
)
server <- function(input, output) {
output$my_slick <- renderSlickR({
slickR(
my_images,
slideId = 'slick_images',
width='90%'
)
})
}
shinyApp(ui, server)
If you want to get the name of the selected image in Shiny, add a line in the script:
tags$script('var my_images = ["image1.png","image2.png","image3.png"];
$("#my_slick").on("click", function(e){
var slideClicked = $(this).find(".slick-active").attr("data-slick-index");
Shiny.setInputValue("selectedImage", my_images[slideClicked]);
document.getElementById("textbox").innerHTML = "Selected image: " + my_images[slideClicked];
});')
Then the names of the selected image is in input$selectedImage.
EDIT
Here is the script for the improvements asked by the OP in a comment:
tags$script('var my_images = ["image1.png","image2.png","image3.png"];
var binary = true;
$("#my_slick").on("click", function(e){
if(e.target.localName == "img"){
if(binary){
var slideClicked = $(this).find(".slick-active").attr("data-slick-index");
Shiny.setInputValue("selectedImage", my_images[slideClicked]);
document.getElementById("textbox").innerHTML = "Selected image: " + my_images[slideClicked];
}else{
document.getElementById("textbox").innerHTML = "";
}
binary = false;
}else{
document.getElementById("textbox").innerHTML = "";
binary = true;
}
});')