Open rpodcast opened 9 years ago
Hey, I wasn't aware of this library, it does look awesome! So much nicer than real alerts indeed. Really good to know.
I'm a bit embarrassed to admit that I haven't actually looked into htmlwidgets yet myself so I'm very unfamiliar with how to run these things... but after playing around with it for a bit it seems to me that something like this will work in a shiny app:
library(shiny)
library(sweetalertR)
runApp(shinyApp(
ui = fluidPage(
actionButton("go", "Go"),
sweetalert(selector = "#go", text = "hello", title = "world")
),
server = function(input, output, session) {
}
))
Which works really nicely :)
Although since I was summoned here, I'll say that I don't love the idea of defining the alert box in the input -- to me, that should be part of the logic rather than part of the UI. It's very possible that my previous code block is not the correct way of using this in a shiny app though. Is it possible to define the alert in the server?
Anyway, just as a fun exercise, I wanted to see if this could be incorporated into shinyjs and it does seem like it, but I won't do it unless there's demand for it. Here's a quick simple example of how to use shinyjs as an interface to sweetalert
library(shiny)
jscode <- "shinyjs.swal = function(params) { swal.apply(this, params); }"
runApp(shinyApp(
ui = fluidPage(
tags$head(
includeScript("https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.0.1/sweetalert.min.js"),
includeCSS("https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.0.1/sweetalert.min.css")
),
shinyjs::useShinyjs(),
shinyjs::extendShinyjs(text = jscode),
textInput("title", "Title"),
textInput("text", "Text"),
selectInput("type", "Type", c("warning", "error", "success")),
actionButton("swal", "Action!")
),
server = function(input, output, session) {
observeEvent(input$swal, {
shinyjs::js$swal(input$title, input$text, input$type)
})
}
))
I hope my presence here is ok @timelyportfolio
@daattali I have been watching shinyjs
and your other projects closely, so I am delighted that you are here. You bring up a couple of very good points. I'll start by saying that I view all of my htmlwidgets
as experimental, and really the whole concept of htmlwidgets
as experimental, so discussions like these are really valuable.
In my mind, htmlwidgets
are designed so that a R
user can leverage and deploy JS/CSS/HTML
straight from R with basically no knowledge of JS/CSS/HTML
. I think shinyjs
is the opposite in that it is designed to make life easier for users who know both R
and JS/CSS/HTML
.
sweetalertR
is tricky since it almost always will require a trigger before it springs into action. Most other htmlwidgets
are expected to just appear. I struggled with the design of sweetalertR
. I did not want to assume that the trigger for sweetalertR would always be a button, so I wanted the R
user to be able to provide any selector, but this of course quickly starts to assume that the user knows some HTML
. In this case, I felt like the user would probably want full customization of the tag
. In katexR
I approached this a little differently with a custom toHTML
function. This makes both the tag
and the katexR
one function where sweetalertR
requires two functions -- a tag
and the sweetalert
.
One other benefit to htmlwidgets
(that I'm not sure is discussed enough) is dependency and conflict management. Adding CSS and JS can be a pain and conflicts can quickly arise. In a lot of my widgets, I'll allow an option just so the dependencies are injected, so in sweetalertR
, you can see in lines. In this case htmlwidgets
and shinyjs
might play very well together. As an example, let's modify your code just a bit. In this simple case, the benefit is less obvious, but with multiple CSS/JS dependencies, you can quickly remove a lot of code.
library(shiny)
library(sweetalertR)
jscode <- "shinyjs.swal = function(params) { swal.apply(this, params); }"
runApp(shinyApp(
ui = fluidPage(
shinyjs::useShinyjs(),
shinyjs::extendShinyjs(text = jscode),
textInput("title", "Title"),
textInput("text", "Text"),
selectInput("type", "Type", c("warning", "error", "success")),
actionButton("swal", "Action!"),
sweetalert()
),
server = function(input, output, session) {
observeEvent(input$swal, {
shinyjs::js$swal(input$title, input$text, input$type)
})
}
This doesn't really apply to sweetalertR
, but I also should mention htmlwidgets
currently are only expected to be outputs and not inputs (see https://github.com/ramnathv/htmlwidgets/issues/19). However, you'll see in some htmlwidgets
code like this in leaflet and parcoords that allows htmlwidgets
to communicate back to R/Shiny
.
I'll stop for now, but happy to continue the discussion.
@thercast did the discussion above help?
Off topic, but I enjoyed the couple of episodes of R podcast. Sounds like a new R podcast has started.
Thank you @timelyportfolio and @daattali! I ran the updated example on my personal server and it works great. Unfortunately the Shiny servers I use at work don't have the necessary libraries required by the v8 package (hence I can't use shinyjs::extendShinyjs()
), but I'll work with our IT group to get them installed.
Thanks for your kind feedback on the previous episodes! I'm getting much closer to re-launching the R-Podcast and I'd like to have a lot more interviews than in the previous run. I plan on doing a series of episodes dedicated to Shiny and htmlwidgets, and it would be excellent to have both of you as future guests on the show. If you're interested please send a message to my podcast email account (on my GitHub profile).
@timelyportfolio you're right, the dependency management is actually a really nice feature. I'd just like to raise another reason whyI like being able to call JS functions from the server and not have them completely coupled to a click on a specific tag. In my past life as a webdev I often ran into situations where I want to trigger an action (such as an alert box) based on some logic that is NOT a button press. For example, if the user presses a "Submit" button and there's an error, I'd like to show an error alert box. Or if the user presses on a button but another input field is not valid, you might want to trigger the alert. I'm not sure if you agree that it's a useful use of alert boxes -- maybe for 99% of shiny users having an alert box on click is all they need.
@thercast it looks like you started these podcasts before I even knew what R is, long time! I did see your last one with Yihui a few months ago, it's a good idea, I would very much support having more episodes.
Thank you both (timelyportfolio and daattali) for publishing workable examples for Shiny. I am a huge fan of shinyjs (daattali you rock!) and happy to see that it can help as well.
SWAL is something I was looking for quite a long time! Happy to use and advertise it!!! Thank you all once again
@daattali is there any way to pass named arguments to function declared through shinyjs::extendShinyjs()? E.g. it would be great to shinyjs::js$swal(title = input$title) but it reports "SweetAlert: SweetAlert expects at least 1 attribute!"
Yes, you can pass named arguments. What you cannot do with shinyjs is use both named and unnamed arguments in the same function call. See this section of the README for more info.
The error you were seeing was an error on sweetalert's part, not coming from shinyjs, but it's happening because in my previous code I called sweetalert using swal.apply(this, params)
, and params
assumed that it was an array with those 3 elements. Anyway, here's a basic example to show you how to that you can call swal with named argument instead
library(shiny)
jscode <- "
shinyjs.swal = function(params) {
var defaultParams = {
title : null,
text : null,
type : null
};
params = shinyjs.getParams(params, defaultParams);
swal(params);
}"
runApp(shinyApp(
ui = fluidPage(
tags$head(
includeScript("https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.0.1/sweetalert.min.js"),
includeCSS("https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.0.1/sweetalert.min.css")
),
shinyjs::useShinyjs(),
shinyjs::extendShinyjs(text = jscode),
textInput("title", "Title"),
textInput("text", "Text"),
selectInput("type", "Type", c("warning", "error", "success")),
actionButton("submit", "Action!")
),
server = function(input, output, session) {
observeEvent(input$submit, {
shinyjs::js$swal(title = input$title, text = input$text, type = input$type)
})
}
))
Thank you daattali, you are a star! It works. And you are right I have missed this passage in your Read.Me Thank you once again!
Hello, Thank you very much @daattali & @timelyportfolio for for shinyjs and sweetalertR. I am trying to use swal in a shinydashboard. I would like to have an alert allowing me to change tabs. Unfortunately, I am not able to make it work. Here is what I was able to come up with. I would like to have the confirm button to switch tab.
library(shiny)
jscode <- "
shinyjs.swal = function(params) {
var defaultParams = {
title : null,
text : null,
type : null
};
params = shinyjs.getParams(params, defaultParams);
swal(params);
}"
ui <- shinydashboard::dashboardPage(
shinydashboard::dashboardHeader(title = "AXA Geocoding"),
shinydashboard::dashboardSidebar(
tags$head(
tags$script(src = "resetInputFile.js"),
tags$script(src = "https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.0.1/sweetalert.min.js"),
tags$link(rel = "stylesheet", type = "text/css", href = "https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.0.1/sweetalert.min.css")
),
shinydashboard::menuItem("Tab 1", tabName = 'tab1'),
shinydashboard::menuItem("Tab 2", tabName = 'tab2')
),
shinydashboard::dashboardBody(
shinyjs::useShinyjs(),
shinyjs::extendShinyjs(text = jscode),
shinydashboard::tabItems(
shinydashboard::tabItem(tabName = 'tab1',
actionButton("submit1", "Action Tab1!")
# function(){
# "$($('#nav a')[1]).tab('show');"
# })
),
shinydashboard::tabItem(tabName = 'tab2',
actionButton("submit2", "Action Tab2")
))
)
)
server <- function(input, output, session) {
observeEvent(input$submit1, {
shinyjs::js$swal(title = 'Computation completed',
type = 'success', showCancelButton = T,
confirmButtonText = 'Go to result page.',
cancelButtonText = 'Stay on computation tab.')
})
}
shinyApp(ui = ui, server = server)
It doesn't look like you have any code here that defines what to do when the confirm button vs cancel button is clicked. I wrote the code above after looking at the very basic SWAL documentation and just wrote it quickly to make it show a popup. If you want to have functionality happen when you click the buttons, you'd have to read their documentation to see how to do that.
@daattali. Thank you for your answer. My mistake, the following code is showing how I would do that using javascript:
library(shiny)
jscode <- "
shinyjs.swal = function(params) {
var defaultParams = {
title : null,
text : null,
type : null
};
params = shinyjs.getParams(params, defaultParams);
swal(params);
}"
ui <- shinydashboard::dashboardPage(
shinydashboard::dashboardHeader(title = "AXA Geocoding"),
shinydashboard::dashboardSidebar(
tags$head(
tags$script(src = "resetInputFile.js"),
tags$script(src = "https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.0.1/sweetalert.min.js"),
tags$link(rel = "stylesheet", type = "text/css", href = "https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.0.1/sweetalert.min.css")
),
shinydashboard::menuItem("Tab 1", tabName = 'tab1'),
shinydashboard::menuItem("Tab 2", tabName = 'tab2')
),
shinydashboard::dashboardBody(
shinyjs::useShinyjs(),
shinyjs::extendShinyjs(text = jscode),
shinydashboard::tabItems(
shinydashboard::tabItem(tabName = 'tab1',
actionButton("submit1", "Action Tab1!")
),
shinydashboard::tabItem(tabName = 'tab2',
actionButton("submit2", "Action Tab2")
))
))
server <- function(input, output, session) {
observeEvent(input$submit1, {
shinyjs::js$swal(title = 'Computation completed',
type = 'success', showCancelButton = T,
confirmButtonText = 'Go to result page.',
cancelButtonText = 'Stay on computation tab.', function(){
"$($('#nav a')[1]).tab('show');"
})
})
}
shinyApp(ui = ui, server = server)
it returns the following error:
Error in : shinyjs: you cannot mix named and unnamed arguments in the same function call (function: shinyjs::js$swal)
@csese It doesn't look like you fully understand either how my sample code above works or how extendShinyjs()
works. By calling js$swal()
, the shinyjs.swal()
function is called. If you look at the JS code, that function expects 3 parameters: title, text, type. You're trying to pass a bunch of other parameters and it doesn't know what to do with them. I think those other parameters in your R code are parameters you intend on having the javascript call, not R.
I think this is closer to the code you want (notice that after clicking the confirm button I'm just showing an alert message because your javascript code to switch tabs doesn't seem to work, but that's a purely javascript question)
jscode <- "
shinyjs.swal = function(params) {
var defaultParams = {
title : null,
text : null,
type : null
};
params = shinyjs.getParams(params, defaultParams);
swal({title : params.title, text : params.text, type : params.type,
showCancelButton : true, confirmButtonText : 'Go to result page.',
cancelButtonText : 'Stay on computation tab.'},
function(){ alert('switch'); });
}"
ui <- shinydashboard::dashboardPage(
shinydashboard::dashboardHeader(title = "AXA Geocoding"),
shinydashboard::dashboardSidebar(
tags$head(
#tags$script(src = "resetInputFile.js"),
tags$script(src = "https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.0.1/sweetalert.min.js"),
tags$link(rel = "stylesheet", type = "text/css", href = "https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.0.1/sweetalert.min.css")
),
shinydashboard::menuItem("Tab 1", tabName = 'tab1'),
shinydashboard::menuItem("Tab 2", tabName = 'tab2')
),
shinydashboard::dashboardBody(
shinyjs::useShinyjs(),
shinyjs::extendShinyjs(text = jscode),
shinydashboard::tabItems(
shinydashboard::tabItem(tabName = 'tab1',
actionButton("submit1", "Action Tab1!")
),
shinydashboard::tabItem(tabName = 'tab2',
actionButton("submit2", "Action Tab2")
))
))
server <- function(input, output, session) {
observeEvent(input$submit1, {
shinyjs::js$swal(title = 'Computation completed',
type = 'success')
})
}
shinyApp(ui = ui, server = server)
Also, the file resetInputFile.js seems to be unrelated but because it's in the code you include it keeps resulting in errors for us.
@daattali Thank you for your explanations.
Here is a working code to switch tab using sweetalert
.
jscode <- "
shinyjs.swal = function(params) {
var defaultParams = {
title : null,
text : null,
type : null
};
params = shinyjs.getParams(params, defaultParams);
swal({title : params.title, text : params.text, type : params.type,
showCancelButton : true, confirmButtonText : 'Go to result page.',
cancelButtonText : 'Stay on computation tab.'},
function(){ $tablinks = $('ul.sidebar-menu').find('a').filter('[data-value=tab2]');
$tablinks.tab('show');
});
}"
header <- shinydashboard::dashboardHeader(title = "AXA Geocoding")
sidebar <- shinydashboard::dashboardSidebar(
tags$head(
tags$script(src = "https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.0.1/sweetalert.min.js"),
tags$link(rel = "stylesheet", type = "text/css",
href = "https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.0.1/sweetalert.min.css")
),
sidebarMenu(id = 'sidebarmenu',
shinydashboard::menuItem("Tab 1", tabName = 'tab1'),
shinydashboard::menuItem("Tab 2", tabName = 'tab2'))
)
body <- shinydashboard::dashboardBody(
shinyjs::useShinyjs(),
shinyjs::extendShinyjs(text = jscode),
shinydashboard::tabItems(
shinydashboard::tabItem(tabName = 'tab1',
actionButton("submit1", "Action Tab1!")),
shinydashboard::tabItem(tabName = 'tab2',
actionButton("submit2", "Action Tab2")))
)
ui <- shinydashboard::dashboardPage(
header = header,
sidebar = sidebar,
body = body
)
server <- function(input, output, session) {
observeEvent(input$submit1, {
shinyjs::js$swal(title = 'Computation completed',
type = 'success')
})
}
shinyApp(ui = ui, server = server)
Is there a way to close the shinyjs::js$swal messages with a line of code after finishing a long calculation? trying to figure it out but no luck so far:
see also: SO question
jscodeswal <- c(
"shinyjs.swalGIF= function(params) {
var defaultParams = {title: null, html : null, imageUrl: null };
params = shinyjs.getParams(params, defaultParams);
swal({title : params.title, html : params.html, imageUrl : params.imageUrl,
animation : false,
showConfirmButton : false,
showCancelButton : false,
allowOutsideClick: false,
allowEscapeKey: false
});
};"
)
if (interactive()) {
require(shiny)
require(shinyalert)
require(shinyjs)
shinyApp(
ui = fluidPage(
shinyjs::useShinyjs(),
shinyjs::extendShinyjs(text = jscodeswal),
tags$head(
includeScript("https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.0.1/sweetalert.min.js"),
includeCSS("https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.0.1/sweetalert.min.css")
),
actionButton("btn", "Click me")
),
server = function(input, output, session) {
observeEvent(input$btn, {
# Show a simple modal
shinyjs::js$swalGIF(title = '<span style="color:#339FFF; font-size: 30px">Test message<span>',
html = paste('<br><span style = "font-weight: normal; color:#797979">Please close me when done!<br><br>'),
imageUrl = 'https://media.giphy.com/media/4HgDESJLG1JSmqTOnH/giphy.gif')
# close me after finish long calculation
})
}
)
}
Hello, I just came across your blog entry on
sweetalertR
and it looks great! I'd like to incorporate this functionality in some of my Shiny applications I'm developing at work. What would be the best way to use this in a Shiny app? Ideally it would be triggered after the user hits an action button much like what you did in your R markdown example. This example from the Shiny gallery uses a javascript file called message-handler.js to call the basicalert
function, but I'm wondering if there's an easy way to swap that with the functions offered in this package? I know @daattali has created the excellent shinyjs package which contains a function calledinfo
to display normal alerts, so I guess another approach might be to use itsextendShinyjs
paradigm but I'm pretty new to using customized javascript solutions in Shiny Apps. Sorry for the long text!