From 4dd93d442b7d7c62088d93964b4111696fc70a78 Mon Sep 17 00:00:00 2001
From: yohann
I’ll focus on a specific AI tool that can significantly assist R developers: GitHub Copilot. Copilot offers a variety of features, from code generation to programming assistance, automatic documentation, and problem-solving.
+I’ll also give a quick feedback about my usage of ChatGPT.
+GitHub Copilot is an AI tool that assists developers in their daily work by generating code and other code-related elements (such as documentation) based on context. Copilot can be used as an extension for VSCode. Since the release of version 2023.09.0, GitHub Copilot is also available in RStudio. Copilot can be used to generate R code, comments, unit tests, documentation, and more.
+To use GitHub Copilot, you need a GitHub account and a Copilot license. This tool is paid (see pricing options), but students, teachers, and maintainers of popular open-source projects can get a free license.
+To use GitHub Copilot in RStudio, install the latest version of RStudio (2023.09.0 or later). Once installed, activate Copilot by going to Tools > Global Options > Copilot
and checking the box Enable GitHub Copilot
.
If you want Copilot to base its suggestions not only on the current file but also on other project files, check the box Index project files with GitHub Copilot
.
To use GitHub Copilot in VSCode, install the GitHub Copilot extension. Once the extension is installed, enable Copilot by logging into your GitHub account.
+Currently, GitHub Copilot is not available in Positron.
+We’ll review GitHub Copilot’s features for R development. Some features are available in both RStudio and VSCode, while others can only be used in VSCode.
+Let’s say we’re developing an R package and want to write a function to clean text by removing spaces and special characters.
+We start with a comment describing the function’s intent and provide an empty function skeleton:
+# Function to clean text by removing spaces and special characters
+clean_text <- function(text) {
+}
After a few seconds, Copilot suggests an implementation for clean_text
. Suggestions appear in gray and can be accepted by pressing the Tab
key.
The initial function comment isn’t mandatory, but it helps Copilot better understand the context and suggest more relevant code.
+Code completion also works seamlessly in VSCode.
+ +Interestingly, in both cases, a step to convert text to lowercase is suggested, even though I didn’t request it. This could be useful, but make sure this step fits your use case. Remember, Copilot isn’t perfect and may sometimes generate incorrect or inappropriate code. Always review and modify the generated code as necessary.
+You can continue in RStudio to generate the function’s documentation with Roxygen tags, for instance. Again, starting with a comment indicating your intention allows Copilot to suggest documentation.
+ +RStudio usage is limited to code completion, so we’ll switch to VSCode to showcase more advanced features and a fuller development experience.
+There are multiple ways to access Copilot’s interface in VSCode. Right-clicking on an open file allows you to access the Copilot
option in the context menu. You can also use the shortcut Ctrl + Shift + P
(on Windows) to open the command palette, then type Copilot
to access the interface. Recently, a Copilot icon was also added to the VSCode toolbar.
Copilot can be used as a chatbot to get information on functions or packages, or to write code, as we did before.
+ + +Again, providing as much context as possible will lead to more relevant suggestions.
+You can also use the chat to modify the code Copilot initially generated, for example, to change the packages used.
+ +Copilot can also explain code. For example, if you don’t understand some code, you can ask Copilot to explain it.
++
Note: This feature is also accessible via chat by typing “/explain”.
+Following the same process, select the “Fix” option to detect errors in the code. Here, Copilot didn’t detect an error but suggested an enhancement. Our code didn’t initially trim leading and trailing spaces. Copilot offers a solution to fix this.
+ +Note: This feature is also accessible via chat by typing “/fix”.
+The “Review and Comment” feature suggests improvements for style or performance.
+ +The “Generate Doc” feature creates a Roxygen documentation skeleton for the function. The generated documentation is often generic, so you’ll usually need to complete it, particularly for parameter explanations or reproducible examples. For instance, it may omit an @export
tag, which could cause issues during a devtools::check()
of your package!
Note: This feature is also accessible via chat by typing “/doc”.
+The “Generate Tests” option generates unit tests for the function. It works well but has the drawback of adding the test file in the package’s R/
folder. You’ll need to move the content into a chosen file in the testthat/tests/
folder.
Note: This feature is also accessible via chat by typing “/tests”.
+{shiny}
?Though we’ve focused on R package development, Copilot can also be used to develop Shiny applications. For example, to generate a simple Shiny app, start with a comment describing the app, and let Copilot generate the code. This is very useful for quickly setting up a UI, but as always, you’ll need to refine the code to meet specific needs.
++
For a Shiny development assistant, you can also check out ShinyAssistant.
+GitHub Copilot is a powerful tool that can greatly accelerate R package development. It can generate code, documentation, unit tests, and more. However, remember that Copilot isn’t perfect and can sometimes produce incorrect or unsuitable code. It’s essential to review and adjust the generated code as needed. It’s equally important to revise generated documentation and tests for accuracy and relevance.
+To ensure sufficient unit tests, it’s recommended to check package coverage using the {covr}
package.
Moreover, there’s no magic behind this tool—GitHub Copilot bases its suggestions on the current file’s content and other project files. The more precise the context, the more relevant the suggestions will be. Taking extra care with function and parameter names makes suggestions as suitable as possible (this is true for development even without Copilot!).
+I also often rely on ChatGPT for R-programming related tasks.
+More often, I’ll use ChatGPT in advance of a task, to compare its suggestions with the way I would have approached the problem. This allows me to see if I’ve forgotten something, or if I couldn’t have done things differently.
+I’ll also use his knowledge to give me information on a package I don’t know, or to give me examples of code. It’s a great complement to the official documentation.
+In the same way, I’ll ask him to explain errors I encounter, or concepts I don’t understand.
+Finally, as with Github Copilot, the key lies in the quality of the question asked. The more precise the question, the more relevant the answer.
+I often tend to start my queries with: “I’m an R developer and I prefer using tidyverse packages. I’d like to know what I can do to…”. Telling him if you’re a user of specific packages can help refine the suggestions.
+Explain how a conversational assistant can answer specific questions, help with debugging, generate code examples and provide detailed explanations of errors or package usage.
+I recently discovered that some R-specific GPT’s are available. They are trained on R-specific data and can provide more relevant answers to R-related questions. I haven’t had the opportunity to test them yet, but I’m looking forward to it.
+Here is the way to access them:
+You should click on “Explore GPTs” in the ChatGPT interface, then type “R programming” in the search menu.
+ + + +Among the many R-specific GPT’s available, I recommend using R Wizard, which seems to be the most popular among R developers.
+For R programmers, GitHub Copilot and ChatGPT can complement each other effectively. Copilot shines for in-context code generation, while ChatGPT is ideal for answering questions, exploring R libraries, and understanding errors. By combining these tools, R developers can boost productivity, streamline repetitive coding tasks, and expand their knowledge—all while enhancing their coding efficiency and accuracy.
+The main takeaway is that these tools are here to help you, not replace you. They can save you time and provide new insights, but they can’t replace your expertise and creativity. Always review the generated code, documentation, and tests to ensure they meet your project’s requirements.
+Have fun !
+I also thank my friend Arthur Bréant for giving me precious tips on how to use the tools presented in this article.
+ + + +Don’t worry! Here’s a short video to sum it all up:
+ +Enjoy 🍿
+ + + + ]]>In this article, we present the different ways to incorporate data (broadly defined) into an R package. We cover the three directories used for storing data and explain how to access them, whether you are a package user or a developer. Finally, we discuss best practices for documenting these data.
+Including data in a package can be useful for several reasons:
+Here, “data” should be interpreted broadly. It includes tabular data typically used in R (e.g., CSV or XLSX files, or data.frame
objects), but also images, configuration files, articles, code samples, etc.
There are three directories in an R package used to store data: data-raw/
, data/
, and inst/
, each serving a specific purpose and catering to different audiences (developers vs. users).
data-raw/
and data/
PairThe goal here is to make data available to package users, which can be used by the package’s functions or included in the documentation examples. These data will be represented as R objects (e.g., data.frame
, list, etc.).
inst/
DirectoryThis directory allows you to store files without format restrictions: tabular files, code sample scripts, notebooks in Rmd/Qmd format, PDF documentation, etc. There are no limits.
+data-raw/
+ data/
Use case: You want to make data available to package users that can be used by the package’s functions. The goal is to provide native access from the package’s functions. You’re probably familiar with preloaded datasets in R like mtcars
or iris
; this follows the same principle.
data-raw/
The data-raw/
folder is used to store scripts for preparing the data. Files in this folder are not included in the final package installed on the user’s computer but contain the code needed to generate the datasets that will later be included in the package.
data/
Once prepared in data-raw/
, the data is stored in the data/
folder. Files in this folder are included in the final package and are accessible to users. The files are stored in .rda
format and are loaded when a user runs library(mypackage)
.
Create the data-raw/
folder using the command usethis::use_data_raw("my_dataset_demo")
. This command creates a my_dataset_demo.R
file in the data-raw/
folder.
Prepare the dataset in the my_dataset_demo.R
file:
# Create a sample of the "starwars" dataset from the dplyr package
+# See https://github.com/tidyverse/dplyr/tree/main/data-raw and https://github.com/tidyverse/dplyr/tree/main/data
+library(dplyr)
+library(readr)
+starwars_raw <- read_csv("data-raw/starwars.csv")
+starwars_sample <- starwars_raw |>
+ sample_n(size = 10)
+usethis::use_data(starwars_sample, overwrite = TRUE)
After running the command usethis::use_data(starwars_sample, overwrite = TRUE)
, you’ll see a file named starwars_sample.rda
in the data/
folder.
There’s still some work to do: now we need to document the dataset. For this, we will use the {checkhelper}
package.
checkhelper::use_data_doc("starwars_sample")
This creates a doc_starwars_sample.R
file in the package’s R/
folder. The file contains the dataset’s documentation. You can now edit this file to add more information about the dataset
`r #' starwars_sample #' #' Description. #' #' @format A data frame with 10 rows and 14 variables: #' \describe{ #' \item{ name }{ The character's name } #' \item{ height }{ numeric } #' \item{ mass }{ numeric } #' \item{ hair_color }{ character } #' \item{ skin_color }{ character } #' \item{ eye_color }{ character } #' \item{ birth_year }{ numeric } #' \item{ sex }{ character } #' \item{ gender }{ character } #' \item{ homeworld }{ character } #' \item{ species }{ character } #' \item{ films }{ character } #' \item{ vehicles }{ character } #' \item{ starships }{ character } #' } #' @source Source "starwars_sample"
While the overall structure of the file should be preserved, you can edit the description, format, and source information as needed.
Finally, generate the LaTeX documentation using the command devtools::document()
or attachment::att_amend_desc()
.
Once your package is installed and loaded, you can access the dataset using the command data("starwars_sample")
.
inst/
Use case: You want to store files intended for use only in unit tests or to share additional documentation (e.g., a scientific article).
+ +Create the inst/
folder at the package root: dir.create(here::here("inst"))
.
Place the desired files in the folder.
Install the package.
The files are now accessible using a special function: system.file()
, which points to the root of the inst/
directory. For example, to access a file named article.pdf
in the inst/
folder, you would use system.file("article.pdf", package = "mypackage")
. If the file is in a subfolder called “doc,” you would use system.file("doc", "article.pdf", package = "mypackage")
.
Note: system.file()
does not read a file; it only returns the file path.
You now know all about incorporating data into an R package. You’ve learned how to store data in the data-raw/
, data/
, and inst/
directories and make it accessible from the package functions. You’ve also learned how to document these data to make them usable for package users.
Whether the data is intended for users or developers, you now have all the tools to enrich your R package with data. Happy coding!
+ + +If you missed my first article on integrating JavaScript libraries into a {shiny} application I invite you to read it before diving into this one.
+Crucial concepts are covered and will not be repeated here. We particularly think about:
+{shiny}
application are addressed, including passing data from R to JavaScript and the differences in expected data formats.{shiny}
app created with {golem}
To get an overview of the possibilities offered by Chart.js, visit the official page: https://www.chartjs.org/docs/latest/samples/information.html
+The following sections assume that you have already created a {shiny}
app with {golem}
.
If this is not the case and you want to learn more about {golem}
, I invite you to consult the official documentation.
To add Chart.js to your {shiny}
app, you will need to find a way to incorporate the necessary files for its operation into your application. As we saw in our previous article, two solutions are possible.
We will use the “CDN” method here.
+Go to the “Getting Started” section of Chart.js documentation .
+We retrieve the CDN URL and store this information for later use.
+After creating the skeleton of an application via {golem}
, we will add the Chart.js dependency.
Let’s open the R/app_ui.R
file of our application and add the link we copied earlier into the body of the golem_add_external_resources()
function.
golem_add_external_resources <- function() {
+ add_resource_path(
+ "www",
+ app_sys("app/www")
+ )
+
+ tags$head(
+ favicon(),
+ bundle_resources(
+ path = app_sys("app/www"),
+ app_title = "chartJS"
+ ),
+ # Add here other external resources
+ # for example, you can add shinyalert::useShinyalert()
+ # Chart.js
+ tags$script(src = "https://cdn.jsdelivr.net/npm/chart.js")
+ )
+}
The “Getting Started” section previously consulted to retrieve the CDN link indicates that it is necessary to incorporate the HTML <canvas>
tag into our application to display a Chart.js chart. We add this element to the R/app_ui.R
file of our application.
app_ui <- function(request) {
+ tagList(
+ # Leave this function for adding external resources
+ golem_add_external_resources(),
+ # Your application UI logic
+ fluidPage(
+ h1("golemchartjs"),
+ tags$div(
+ tags$canvas(id="myChart")
+ )
+ )
+ )
+}
To verify that Chart.js is properly imported into our application, we run our app with golem::run_dev()
, and the rest will take place in the web browser.
NB: The following screenshots were taken using the Google Chrome browser.
+In the window of our application, right-click and then select “Inspect”. In the new window that opens, choose the “Console” tab and type the command to generate a Chart.js chart, as indicated once again in the “Getting Started” section between the HTML script
tags.
The code to copy and paste into the console is the following:
+ const ctx = document.getElementById('myChart');
+
+ new Chart(ctx, {
+ type: 'bar',
+ data: {
+ labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
+ datasets: [{
+ label: '# of Votes',
+ data: [12, 19, 3, 5, 2, 3],
+ borderWidth: 1
+ }]
+ },
+ options: {
+ scales: {
+ y: {
+ beginAtZero: true
+ }
+ }
+ }
+ });
The chart from the demo page appears as expected! 🎉
+We can move on! 😊
+The application code for this step is available here: https://github.com/ymansiaux/golemchartjs/tree/step1
+The code used previously allowed us to verify that Chart.js was properly imported into our application. Now, we will see how to create a Chart.js chart from our {shiny}
application. The goal is to produce bar charts for various datasets with customizable options based on user choices.
Let’s revisit the code executed previously:
+ const ctx = document.getElementById('myChart');
+
+ new Chart(ctx, {
+ type: 'bar',
+ data: {
+ labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
+ datasets: [{
+ label: '# of Votes',
+ data: [12, 19, 3, 5, 2, 3],
+ borderWidth: 1
+ }]
+ },
+ options: {
+ scales: {
+ y: {
+ beginAtZero: true
+ }
+ }
+ }
+ });
We could imagine passing the labels
, label
, data
, and borderWidth
elements as function parameters.
Let’s go! 🚀
+We saw in our previous article that the way to call JavaScript code from R is to use a “JS handler”. To do this, go to the dev/02_dev.R
file! We add the following line in the “External Resources” section:
golem::add_js_handler("barchartJS")
We fill in the skeleton by indicating “barchartJS” as the name of our handler and adding the JavaScript code we saw previously.
+$(document).ready(function () {
+ Shiny.addCustomMessageHandler("barchartJS", function (arg) {
+ const ctx = document.getElementById("myChart");
+
+ new Chart(ctx, {
+ type: "bar",
+ data: {
+ labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
+ datasets: [
+ {
+ label: "# of Votes",
+ data: [12, 19, 3, 5, 2, 3],
+ borderWidth: 1,
+ },
+ ],
+ },
+ options: {
+ scales: {
+ y: {
+ beginAtZero: true,
+ },
+ },
+ },
+ });
+ });
+});
We replace the labels
, label
, data
, and borderWidth
parameters, which are hardcoded here, with the future elements passed as arguments to our handler. The notation to use here will be arg.param_name
to access the values passed by our {shiny}
application. The .
notation is a JavaScript convention for accessing properties of an object. To draw a parallel with R, it’s somewhat like using arg$param_name
.
At the beginning of our handler, we add a call to the console.log()
function to check the contents of the arg
element from the JS console. This will allow us to verify that the elements passed from R are correctly transmitted to our handler.
$( document ).ready(function() {
+ Shiny.addCustomMessageHandler('barchartJS', function(arg) {
+ console.log(arg);
+ const ctx = document.getElementById('myChart');
+
+ new Chart(ctx, {
+ type: 'bar',
+ data: {
+ labels: arg.labels,
+ datasets: [{
+ label: arg.label,
+ data: arg.data,
+ borderWidth: arg.borderWidth
+ }]
+ },
+ options: {
+ scales: {
+ y: {
+ beginAtZero: true
+ }
+ }
+ }
+ });
+ })
+});
We will add elements to the R/app_ui.R
file to generate the parameters to be passed to our handler:
arg.labels
will be a vector of 5 character strings, randomly chosen from the letters of the latin alphabet.arg.label
will be a character string, randomly chosen from the letters of the latin alphabet.arg.data
will be a vector of 5 integer numbers, randomly chosen between 1 and 100.arg.borderWidth
will be an integer, randomly chosen between 1 and 5.The display of the chart will be triggered by clicking a “Show Barplot” button.
+Here is the content of our R/app_ui.R
file:
app_ui <- function(request) {
+ tagList(
+ # Leave this function for adding external resources
+ golem_add_external_resources(),
+ # Your application UI logic
+ fluidPage(
+ h1("golemchartjs"),
+ actionButton(
+ inputId = "showbarplot",
+ label = "Show Barplot"
+ ),
+ tags$div(
+ tags$canvas(id = "myChart")
+ )
+ )
+ )
+}
And the content of the R/app_server.R
file :
app_server <- function(input, output, session) {
+ observeEvent(input$showbarplot, {
+ app_labels <- sample(letters, 5)
+ app_label <- paste0(sample(letters, 10), collapse = "")
+ app_data <- sample(1:100, 5)
+ app_borderWidth <- sample(1:5, 1)
+
+ golem::invoke_js(
+ "barchartJS",
+ list(
+ labels = app_labels,
+ label = app_label,
+ data = app_data,
+ borderWidth = app_borderWidth
+ )
+ )
+ })
+}
Here are the key points to remember:
+golem::invoke_js()
is the name of the JavaScript handler.arg
object of our handler.Let’s run our application with golem::run_dev()
and verify that everything works as expected!
Congratulations! 👏
+In addition to the displayed chart, we can see that the JavaScript console in the browser correctly shows the content of the arg
object, including its 4 sub-elements: labels
, label
, data
, and borderWidth
.
And if you click the button again, what happens?
+ +The chart does not update; it remains stuck on the first chart! 😮
+The JavaScript console indicates that the arg
object has indeed been updated, but the chart does not refresh. Additionally, an error message appears in the JavaScript console: “Error: Canvas is already in use. Chart with ID ‘0’ must be destroyed before the canvas with ID ‘myChart’ can be reused.”
Let’s try to understand what’s happening: in the R/app_ui.R
file, we added a canvas
element with the ID “myChart” (with tags$canvas(id = "myChart")
). This element is used to display the chart. When we click the “Show Barplot” button, a new chart is generated and displayed in this element. However, the previous chart is not destroyed, and the error message indicates that the “canvas” is already in use.
The application code for this step is available here: https://github.com/ymansiaux/golemchartjs/tree/step2
+To find the answer, we need to refer back to the Chart.js documentation. We can read in the “.destroy()” section that in order to reuse the HTML “canvas” element for displaying a new chart, it is necessary to destroy the previous chart.
+There is also a command “.update()” for updating an existing chart. This method seems more appropriate here, as we are using the same type of chart, with only a few parameters changing. The .update()
method allows updating an existing chart without having to destroy and recreate it, which will be less “brutal” visually (with a chart disappearing and then reappearing). However, the .destroy()
method should be kept in mind for cases where we want to radically change the type of chart, for example.
Updating a chart implies that a chart has already been generated once. Therefore, we need to modify our JavaScript handler to account for this and find a way to detect the existence of a chart on our page. For this, we will refer again to the Chart.js documentation, particularly the getChart
method: https://www.chartjs.org/docs/latest/developers/api.html#static-getchart-key.
The command to use is in the following form: const chart = Chart.getChart("canvas-id");
. According to the documentation, if the chart exists, the variable chart
will contain the Chart.js object associated with the HTML “canvas” element. If the chart does not exist, the variable chart
will be undefined
.
For this command to work, we need to replace “canvas-id” with the ID of our “canvas”, which is “myChart” here: const chart = Chart.getChart("myChart");
Let’s restart our application. We indeed find that the chart
object is undefined
as long as the chart has not been created, and it correctly reflects this status afterwards.
We can adapt our code as follows:
+If chart
is undefined
, we create a new chart.
If chart
is not undefined
, we update the existing chart.
We adapt our handler by referring to the documentation for the .update()
method: https://www.chartjs.org/docs/latest/developers/api.html#update-mode
$(document).ready(function () {
+ Shiny.addCustomMessageHandler("barchartJS", function (arg) {
+ console.log(arg);
+ const ctx = document.getElementById("myChart");
+
+ const chart = Chart.getChart("myChart");
+
+ if (chart == undefined) {
+ console.log("Creating a new chart");
+ new Chart(ctx, {
+ type: "bar",
+ data: {
+ labels: arg.labels,
+ datasets: [
+ {
+ label: arg.label,
+ data: arg.data,
+ borderWidth: arg.borderWidth,
+ },
+ ],
+ },
+ options: {
+ scales: {
+ y: {
+ beginAtZero: true,
+ },
+ },
+ },
+ });
+ } else {
+ console.log("Updating an existing chart");
+ chart.data.labels = arg.labels;
+ chart.data.datasets[0].label = arg.label;
+ chart.data.datasets[0].data = arg.data;
+ chart.data.datasets[0].borderWidth = arg.borderWidth;
+ chart.update();
+ }
+ });
+});
This example is a bit more complex than those seen so far:
+Retrieve the Chart.js object associated with the HTML “canvas” element using the method Chart.getChart("myChart")
.
Check if this object is undefined
: if it is, use the code that has been working until now to create a new chart.
If it is not undefined
, overwrite the configuration elements you want to update and then use the .update()
method. Note the specifics of handling configuration elements: chart.data.labels = arg.labels
for the labels, chart.data.datasets[0].label = arg.label
for the label, etc. Use .
to access object properties, with each .
allowing access to a deeper level of “depth”. It is also important to note that array indexing starts at 0 in JavaScript, not at 1 like in R.
After all these efforts, let’s see if everything is back in order 😄!
+ +Phew, everything is OK this time! 🥲
+We’ve touched on a more complex case of using a JavaScript library in a {shiny}
application. It is crucial to understand the library’s functioning by delving into the depths of its documentation. Moreover, one of the advantages of using a very popular library is that you can often find help on StackOverflow 😊 (here is an example of using the .destroy()
method).
Feel free to go further in customizing your chart, such as changing the bar colors: https://www.chartjs.org/docs/latest/general/colors.html and https://www.chartjs.org/docs/latest/charts/bar.html.
+The best way to learn is to try reproducing examples from the documentation.
+The application code for this step is available here: https://github.com/ymansiaux/golemchartjs/tree/step3
+We will attempt to create a scatter plot with Chart.js. To develop our code, we will rely on the Chart.js documentation: https://www.chartjs.org/docs/latest/charts/scatter.html.
+As before, our code will be stored in a JS handler. Therefore, we will add a new handler in the dev/02_dev.R
file:
golem::add_js_handler("scatterplotJS")
The documentation is slightly different from that provided for bar charts. We will need to adapt our handler accordingly. We identify an element config
, which will include the type
, data
, and options
elements we have already seen. There is also a data
element containing datasets
and labels
.
We will fill in the skeleton of our handler with the JavaScript code from the Chart.js documentation. Initially, we will leave out the “update” part.
+$(document).ready(function () {
+ Shiny.addCustomMessageHandler("scatterplotJS", function (arg) {
+ const ctx = document.getElementById("myChart2");
+
+ const data = {
+ datasets: [
+ {
+ label: "Scatter Dataset",
+ data: [
+ {
+ x: -10,
+ y: 0,
+ },
+ {
+ x: 0,
+ y: 10,
+ },
+ {
+ x: 10,
+ y: 5,
+ },
+ {
+ x: 0.5,
+ y: 5.5,
+ },
+ ],
+ backgroundColor: "rgb(255, 99, 132)",
+ },
+ ],
+ };
+
+ const config = {
+ type: "scatter",
+ data: data,
+ options: {
+ scales: {
+ x: {
+ type: "linear",
+ position: "bottom",
+ },
+ },
+ },
+ };
+ new Chart(ctx, config);
+ });
+});
Our JS handler “scatterplotJS” is ready! We need to add the “div” and “canvas” to the UI to display the generated chart. We need to modify the HTML ID of our “canvas” to avoid any conflict with the bar chart. It will be named “myChart2” here.
+Note that there is a slightly different syntax compared to the code used for the bar chart, where the call to “new Chart” was made directly with the data
and options
elements. Here, we store these elements in data
and config
variables before passing them to new Chart
.
Next, we add the following to the R/app_ui.R
file:
h1("Scatterplot"),
+actionButton(
+ inputId = "showscatterplot",
+ label = "Show Scatterplot"
+),
+tags$div(
+ tags$canvas(id = "myChart2")
+)
We add the following to the R/app_server.R
file:
observeEvent(input$showscatterplot, {
+ golem::invoke_js(
+ "scatterplotJS",
+ list(
+ )
+ )
+ })
Our handler does not use any elements passed from R. However, it is necessary to pass an empty list as an argument to ensure the proper functioning of golem::invoke_js()
.
Let’s run your application with golem::run_dev()
and verify that everything works as expected!
The chart from the documentation works! 🎉
+Now, let’s go further by passing our own data as input.
+The application code for this step is available here: https://github.com/ymansiaux/golemchartjs/tree/step4
+We will use the iris
dataset to generate a scatter plot. We will pass as arguments to our JS handler the data from the Sepal.Length
and Sepal.Width
columns.
As with the bar chart, we will use elements passed from R through the arg
object in JavaScript.
We modify the data
object to include a legend title and, most importantly, the data. To observe the elements passed from R, we add a call to console.log()
.
console.log(arg);
+const data = {
+ datasets: [
+ {
+ label: arg.label,
+ data: arg.data,
+ backgroundColor: "rgb(255, 99, 132)",
+ },
+ ],
+};
As a reminder, in the example from the documentation, the data is passed in the form of an “array of dictionaries”. Each dictionary contains the keys x
and y
for the point coordinates.
data: [{
+ x: -10,
+ y: 0
+ }, {
+ x: 0,
+ y: 10
+ }, {
+ x: 10,
+ y: 5
+ }, {
+ x: 0.5,
+ y: 5.5
+}]
Let’s try to pass the contents of the Sepal.Length
and Sepal.Width
columns via a list. We make the following modification in R/app_server.R
:
observeEvent(input$showscatterplot, {
+ golem::invoke_js(
+ "scatterplotJS",
+ list(
+ label = "My scatterplot",
+ data = list(
+ x = iris$Sepal.Length,
+ y = iris$Sepal.Width
+ )
+ )
+ )
+})
We restart our application, and unfortunately, nothing shows up!
+ +Thanks to the console.log()
call in our handler, we can observe the content of the arg
object in the JavaScript console of the browser. We notice that the data passed is not in the correct format. Here, we get an array of two elements, the first containing the values of Sepal.Length
and the second containing the values of Sepal.Width
, which is not the expected format.
Here, we need to do some work on the R side to transform our data into the expected format.
+If we display a JSON preview of the data we passed as input, indeed the rendering is incorrect.
+jsonlite::toJSON(
+ list(
+ x = iris$Sepal.Length,
+ y = iris$Sepal.Width
+ )
+)
{"x":[5.1,4.9,4.7,4.6,5,5.4,4.6,5,4.4,4.9],"y":[3.5,3,3.2,3.1,3.6,3.9,3.4,3.4,2.9,3.1]}
+For manipulating lists, the {purrr}
package is a top choice.
new_data <- purrr::transpose(
+ list(
+ x = iris$Sepal.Length,
+ y = iris$Sepal.Width
+ )
+)
+jsonlite::toJSON(
+ new_data,
+ auto_unbox = TRUE
+)
[{"x":5.1,"y":3.5},{"x":4.9,"y":3},{"x":4.7,"y":3.2},{"x":4.6,"y":3.1},{"x":5,"y":3.6},{"x":5.4,"y":3.9},{"x":4.6,"y":3.4},{"x":5,"y":3.4},{"x":4.4,"y":2.9},{"x":4.9,"y":3.1}]
+The rendering seems to be more in line with what is expected by Chart.js. Therefore, we will modify our code to pass the data in this manner.
+observeEvent(input$showscatterplot, {
+ golem::invoke_js(
+ "scatterplotJS",
+ list(
+ label = "My scatterplot",
+ data = purrr::transpose(
+ list(
+ x = iris$Sepal.Length,
+ y = iris$Sepal.Width
+ )
+ )
+ )
+ )
+})
Let’s observe the result:
+ +This time it’s good! 😊 We can see in the JavaScript console that the data has indeed been passed in the correct format.
+The application code for this step is available here: https://github.com/ymansiaux/golemchartjs/tree/step5
+Our chart still lacks titles for the axes! To find out how to do this, the documentation comes to our rescue once again: https://www.chartjs.org/docs/latest/axes/labelling.html#scale-title-configuration.
+We need to add a title
object to our existing scales
object. Each axis, “x” and “y”, is an object within the scales
object and can have a title along with its associated parameters (color, font, etc.).
We will add a title
element to the x
object within our scales
object. Several parameters are customizable, and we will need to modify the text
parameter to set the title for each axis and the display
parameter to show them, as this parameter is set to false
by default (note the different boolean notation between JavaScript and R: true/false
VS TRUE/FALSE
).
The documentation sometimes lacks examples, so we can also rely on StackOverflow: https://stackoverflow.com/questions/27910719/in-chart-js-set-chart-title-name-of-x-axis-and-y-axis. However, be careful with the version of Chart.js used, as parameters may vary.
+In our JS handler, we will add an xAxisTitle
parameter and a yAxisTitle
parameter.
const config = {
+ type: 'scatter',
+ data: data,
+ options: {
+ scales: {
+ x: {
+ type: 'linear',
+ position: 'bottom',
+ title: {
+ display: true,
+ text: arg.xAxisTitle
+ }
+ },
+ y: {
+ title: {
+ display: true,
+ text: arg.yAxisTitle
+ }
+ }
+ }
+ }
+ };
Be cautious once again about the syntax difference between JavaScript and R. Parameters are passed in the form display: true
rather than display = TRUE
, for example. Confusing :
with =
can easily occur and result in non-functional code.
In our R/app_server.R
file, we will add the xAxisTitle
and yAxisTitle
elements to the list passed as an argument to our handler.
observeEvent(input$showscatterplot, {
+ golem::invoke_js(
+ "scatterplotJS",
+ list(
+ label = "My scatterplot",
+ data = purrr::transpose(
+ list(
+ x = iris$Sepal.Length,
+ y = iris$Sepal.Width
+ )
+ ),
+ xAxisTitle = "Sepal Length",
+ yAxisTitle = "Sepal Width"
+ )
+ )
+})
And here’s the result in our application:
+ +The application code for this step is available here: https://github.com/ymansiaux/golemchartjs/tree/step6
+Additional modifications can be made to enhance the chart:
+Modify the title
Change the fill color of the points and their border color
Here are the resources used to produce the code that we will present shortly:
+Title: https://www.chartjs.org/docs/latest/configuration/title.html
+The title should be included in a plugins
object, which in turn is included in the options
object.
Point Colors: https://www.chartjs.org/docs/latest/charts/line.html#point-styles
+The color of the points will be managed within the datasets
object.
We will offer users the ability to set the chart title, its color, and the color of the points through shiny
inputs (which will be a good way to revisit the “update”-related issues 😉).
Below is a preview of the chart created here (without functional “update” for now):
+ +The handler code has been completed to account for these new elements:
+$(document).ready(function () {
+ Shiny.addCustomMessageHandler("scatterplotJS", function (arg) {
+ const ctx = document.getElementById("myChart2");
+
+ console.log(arg);
+
+ const data = {
+ datasets: [
+ {
+ label: arg.label,
+ data: arg.data,
+ borderColor: arg.pointBorderColor,
+ backgroundColor: arg.pointBackGroundColor,
+ },
+ ],
+ };
+
+ const plugins = {
+ title: {
+ display: true,
+ text: arg.mainTitle,
+ color: arg.mainTitleColor,
+ },
+ };
+
+ const config = {
+ type: "scatter",
+ data: data,
+ options: {
+ plugins: plugins,
+ scales: {
+ x: {
+ type: "linear",
+ position: "bottom",
+ title: {
+ display: true,
+ text: arg.xAxisTitle,
+ },
+ },
+ y: {
+ title: {
+ display: true,
+ text: arg.yAxisTitle,
+ },
+ },
+ },
+ },
+ };
+ new Chart(ctx, config);
+ });
+});
In R/app_ui.R
, elements have been added to allow the user to pass the necessary parameters:
h1("Scatterplot"),
+textInput(
+ inputId = "scatterplot_title",
+ label = "Scatterplot Title",
+ value = "ChartJS rocks !"
+),
+selectInput(
+ inputId = "title_color",
+ label = "Title Color",
+ choices = c("brown", "orange", "purple"),
+ selected = "brown"
+),
+selectInput(
+ inputId = "points_background_color",
+ label = "Points Background Color",
+ choices = c("red", "blue", "green"),
+ selected = "red"
+),
+actionButton(
+ inputId = "showscatterplot",
+ label = "Show Scatterplot"
+),
+tags$div(
+ tags$canvas(id = "myChart2")
+)
Finally, in R/app_server.R
, we add the necessary elements to pass the parameters to our handler:
observeEvent(input$showscatterplot, {
+ golem::invoke_js(
+ "scatterplotJS",
+ list(
+ label = "My scatterplot",
+ data = purrr::transpose(
+ list(
+ x = iris$Sepal.Length,
+ y = iris$Sepal.Width
+ )
+ ),
+ xAxisTitle = "Sepal Length",
+ yAxisTitle = "Sepal Width",
+ mainTitle = input$scatterplot_title,
+ mainTitleColor = input$title_color,
+ pointBorderColor = "black",
+ pointBackGroundColor = input$points_background_color
+ )
+ )
+ })
The application code for this step is available here: https://github.com/ymansiaux/golemchartjs/tree/step7
+We still need to include the .update()
method to account for updates to shiny
inputs related to the title and the color of the points.
We will use the approach from the previous chart to modify our JS handler accordingly.
+$(document).ready(function () {
+ Shiny.addCustomMessageHandler("scatterplotJS", function (arg) {
+ const ctx = document.getElementById("myChart2");
+
+ console.log(arg);
+
+ const chart2 = Chart.getChart("myChart2");
+
+ if (chart2 == undefined) {
+ console.log("Creating a new chart");
+
+ const data = {
+ datasets: [
+ {
+ label: arg.label,
+ data: arg.data,
+ borderColor: arg.pointBorderColor,
+ backgroundColor: arg.pointBackGroundColor,
+ },
+ ],
+ };
+
+ const plugins = {
+ title: {
+ display: true,
+ text: arg.mainTitle,
+ color: arg.mainTitleColor,
+ },
+ };
+
+ const config = {
+ type: "scatter",
+ data: data,
+ options: {
+ plugins: plugins,
+ scales: {
+ x: {
+ type: "linear",
+ position: "bottom",
+ title: {
+ display: true,
+ text: arg.xAxisTitle,
+ },
+ },
+ y: {
+ title: {
+ display: true,
+ text: arg.yAxisTitle,
+ },
+ },
+ },
+ },
+ };
+ new Chart(ctx, config);
+ } else {
+ console.log("Updating an existing chart");
+ chart2.data.datasets[0].backgroundColor = arg.pointBackGroundColor;
+ chart2.options.plugins.title.text = arg.mainTitle;
+ chart2.options.plugins.title.color = arg.mainTitleColor;
+ chart2.update();
+ }
+ });
+});
Let’s observe the result:
+ +Well done! 🎉
+The application code for this step is available here: https://github.com/ymansiaux/golemchartjs/tree/step8
+We will look to modify the tooltip that appears when hovering over a point on the chart. In addition to changing its title, we want to display the row number from the dataset corresponding to the hovered point, as well as the corresponding values of Sepal.Length
and Sepal.Width
.
Here are the resources used:
+Tooltip title: https://www.chartjs.org/docs/latest/configuration/tooltip.html#tooltip-callbacks
Tooltip content: https://www.chartjs.org/docs/latest/configuration/tooltip.html#tooltip-callbacks and https://www.youtube.com/watch?v=anseX1ePfUw
This part will be more complex than the previous ones. But we will manage it! 💪
+The plugins
object, used previously to manage the chart title, contains a tooltip
element, which in turn contains a callbacks
element. It is within this element that we can modify the title and content of the tooltip. Most tooltip elements can be configured via a function call that takes a context
element as input. This is a JavaScript object that contains several items related to the hovered point. We will explore the content of this object to extract the information we need later when customizing the tooltip content.
We modify our JS handler by including a fixed title (we could also have passed it as a parameter):
+const tooltip = {
+ callbacks: {
+ title: function (context) {
+ return "Tooltip title";
+ },
+ },
+};
+
+const plugins = {
+ title: {
+ display: true,
+ text: arg.mainTitle,
+ color: arg.mainTitleColor,
+ },
+ tooltip: tooltip
+};
Let’s see if it works:
+ +The application code for this step is available here: https://github.com/ymansiaux/golemchartjs/tree/step9
+Let’s proceed with customizing the tooltip content!
+In this step, we will modify the label
parameter in the tooltip
object. To refine our code, we will use the debugger
function, which we haven’t used so far! If you are familiar with using browser()
in R, debugger
is its JavaScript equivalent. It allows you to pause the code execution and open the browser console to explore the arguments passed to a function.
Let’s modify our handler:
+const tooltip = {
+ callbacks: {
+ title: function (context) {
+ return "Tooltip title";
+ },
+ label: function(context) {
+ debugger;
+ }
+ },
+};
+
+const plugins = {
+ title: {
+ display: true,
+ text: arg.mainTitle,
+ color: arg.mainTitleColor,
+ },
+ tooltip: tooltip,
+};
We add a call to the JavaScript debugger
in the label
function of the callbacks
object. We restart our application:
When hovering over a point on the chart, code execution is paused and the browser console opens. We can then explore the content of the context
object passed to the label
function.
We can identify the information that will be useful:
+The row number in the dataset: context.dataIndex
The values of the point: context.formattedValue
We can then construct a customized tooltip (remembering to remove the debugger
call 😉):
const tooltip = {
+ callbacks: {
+ title: function (context) {
+ return "Tooltip title";
+ },
+ label: function (context) {
+ lab =
+ "Line number: " +
+ context.dataIndex +
+ " values: " +
+ context.formattedValue;
+ return lab;
+ },
+ },
+};
+
+const plugins = {
+ title: {
+ display: true,
+ text: arg.mainTitle,
+ color: arg.mainTitleColor,
+ },
+ tooltip: tooltip,
+};
Mission accomplished! 🚀
+The code for this step is available here: https://github.com/ymansiaux/golemchartjs/tree/step10
+After our initial foray into calling JavaScript code from R with the sweetalert2 library, we have now explored using a data visualization library.
+Key takeaways:
+jsonlite::toJSON()
to verify that the data passed is in the format expected by the library.console.log()
or debugger
to see the contents of a JavaScript object passed as an argument to a function.After overcoming some challenging moments, we can see the possibilities offered by JavaScript data visualization libraries. You can achieve a high degree of customization for your charts, and Chart.js offers many features. Documentation, combined with research on discussion forums, can help solve problems that may seem insurmountable at first.
+Feel free to dive into integrating JavaScript libraries into your {shiny}
applications. It can be an excellent way to break new ground and offer interactive and customized charts to your users.
See you soon for new adventures! 🚀
+ + +Enjoy the ultimate comics collection experience with “mycomicslibrary”, designed for true comic book enthusiasts !
+This app allows you to manage your comic book collection. You can add comic books to your collection, rate them, complete information, etc. You can also add comic books to your wishlist if you don’t own them yet!
+ + +Have fun ! 📚
+ + + + ]]>{shiny}
are not very functional or downright austere? Are you looking to implement some feature in your app but it is not available in the {shiny}
toolbox? Take a look at JavaScript!
+JavaScript is a very popular programming language that is often used to add features to web pages. With HTML and CSS, JavaScript is an essential language for web developers. The size of its user community means that if you are looking to implement a particular feature, there is a good chance that someone has already had the same need as you and has shared their code!
+An other positive point (and essential for us in this case) : it is possible to integrate JavaScript libraries into a {shiny}
application to add features that are not available by default. In addition to that, {golem}
will help us to set everything up.
No more excuses to back down, let’s go ! 🚀
+{shiny}
by integrating a JavaScript library is possible!
+{golem}
has several functions to make it easier for us to integrate JavaScript libraries into a {shiny}
app{shiny}
app created with {golem}
Let’s take a look at the possibilities offered by sweetalert2: https://sweetalert2.github.io/
+If you click on “Show normal alert”, you will see a classic alert while clicking on “Show success message”, you will see a sweetalert2 alert.
+The first one has a rather austere design while the second one is more modern and more pleasant to the eye, it will probably offer a better user experience.
+Feel free to play with the different types of alerts offered by sweetalert2 to get an idea of what is possible with this library by visiting the examples section.
+{shiny}
appThe following sections assume that you have already created a {shiny}
app with {golem}
.
If this is not the case and you want to know more about {golem}
, I invite you to consult the official documentation.
To add sweetalert2 to your {shiny}
app, you will need to find a way to incorporate the files needed for its operation into your application.
Two solutions are available to you:
+Use a “CDN” (Content Delivery Network) to load the files from a third-party server. The CDN will be the equivalent of a CRAN for JavaScript libraries. Concretely, we will ask our application to point to the sources of sweetalert2, hosted on a remote server.
Download the files needed for its operation and integrate them directly into your application. If your application is intended to be used on a machine that is not connected to the Internet, you will inevitably have to go through this step.
Don’t panic! We will see both methods
+The sweetalert2 documentation is very well done. You will find all the information you need to integrate the library into your application from the Download section.
+However, you will need to learn how to identify the elements you need to integrate sweetalert2 into your application.
+Looking for the CDN
+In the “Download & Install” section, you will find a link to the sweetalert2 CDN. This is the link that we will have to add to our application in order to use the library.
+When you click on the link, you will arrive on a page that looks like this:
+What we are interested in here is the link in the <script>
tag and the link in the <link>
tag. The link to the file with the .js
extension corresponds to the sweetalert2 JavaScript file. The link to the file with the .css
extension corresponds to the sweetalert2 styles file.
Copy them and let’s go back to our app!
+Let’s open the R/app_ui.R
file of our application and add the two links we copied earlier to the body of the golem_add_external_resources()
function.
golem_add_external_resources <- function() {
+ add_resource_path(
+ "www",
+ app_sys("app/www")
+ )
+
+ tags$head(
+ favicon(),
+ bundle_resources(
+ path = app_sys("app/www"),
+ app_title = "golemsweetalert"
+ ),
+ # sweetalert2
+ tags$script(src = "https://cdn.jsdelivr.net/npm/sweetalert2@11.10.7/dist/sweetalert2.all.min.js"),
+ tags$link(href = "https://cdn.jsdelivr.net/npm/sweetalert2@11.10.7/dist/sweetalert2.min.css",
+ rel="stylesheet")
+
+ )
+}
We find here a call to tags$script
and a call to tags$link
corresponding respectively to the <script>
and <link>
tags seen on the links provided by the CDN.
We copy-paste the elements being careful about the parameter names src
, href
and rel
and remembering to separate them with commas. We are indeed moving from HTML code (where elements are separated by spaces) to R code.
The links identified earlier will also be useful to download the files needed to use sweetalert2.
+The link to the JavaScript file will be passed to the golem::use_external_js_file()
function while the link to the CSS file will be passed to the golem::use_external_css_file()
function.
To keep a record of this, we will save the following commands in the dev/02_dev.R
file in the “External resources” section.
golem::use_external_js_file("https://cdn.jsdelivr.net/npm/sweetalert2@11.10.7/dist/sweetalert2.all.min.js")
+golem::use_external_css_file("https://cdn.jsdelivr.net/npm/sweetalert2@11.10.7/dist/sweetalert2.min.css")
Both files are now present in the inst/app/www
folder of our application project.
Everything is ok, we can continue our journey 👍.
+To check that sweetalert2 is correctly imported into our application, we open our browser and then the development console.
+Before that, let’s launch our app with golem::run_dev()
!
NB: The following screenshots were taken with the Google Chrome browser.
+On the window of our application, we right-click and select “Inspect”. In the new window that opens, we choose the “Console” tab and then type the command Swal.fire("Hello sweetalert2!")
. This console allows us to execute JavaScript code, which will be interpreted by our browser.
If everything went well, we should see a sweetalert2 alert appear! Otherwise, we might have an error message in red (which we will have to learn to decipher, as with an R console :-)).
+It works! 🎉
+Swal.fire(" .... ")
?Thanks to the documentation! By going to the sweetalert2 page, we find many examples of using the library. In this case, to display an alert, you have to use the Swal.fire()
function. As for learning a new R package, we see that documentation (when it exists …) is of paramount importance for the handling of JavaScript libraries.
Now that we have imported sweetalert2 into our application and checked that the import went well, we are going to create a function that will allow us to call sweetalert2 from our application.
+We are going to try to incorporate the “A title with a text under” alert into our application.
+Three elements can be set here: - The title of the alert - The text of the alert - The type of alert (success, error, warning, info, question) and its associated icon
+We can easily imagine how to modify these elements directly in the JavaScript code but we don’t know yet how to create this alert via R code. Let’s go!
+In order to call sweetalert2 from our application, we are going to create a JavaScript file in which we will write a function that will allow us to create an alert.
+Let’s create a inst/app/www/sweet_alert.js
file in which we will paste the code to create the alert selected earlier. We could have created this file manually, but we will take advantage of the features offered by {golem}
to do so.
Let’s go to the dev/02_dev.R
file! We add the following line in the “External Resources” section:
golem::add_js_handler("sweet_alert")
The file name doesn’t matter, but it is important to respect the .js
extension so that the file is correctly interpreted as JavaScript code. We could have also created subfolders if we had had many imports and files to manage. The only prerequisite is that everything is located in the inst/app/www
directory.
We obtain a slightly strange skeleton, which will in fact be the skeleton of a JavaScript function, usable in our {shiny}
application:
$( document ).ready(function() {
+ Shiny.addCustomMessageHandler('fun', function(arg) {
+
+ })
+});
We are going to substitute the term 'fun'
with the name of the function we want to call in our {shiny}
application and add the code to create the sweetalert2 alert.
We therefore obtain the following code:
+$( document ).ready(function() {
+ Shiny.addCustomMessageHandler('alert_title_and_text_under', function(arg) {
+ Swal.fire({
+ title: "The Internet?",
+ text: "That thing is still around?",
+ icon: "question"
+ });
+ })
+});
Our parameters “title”, “text” and “icon” are fixed, we need to find a way to make them vary according to the choices of the user. We can notice the existence of a “arg” parameter in the Shiny.addCustomMessageHandler()
function. It is this parameter that will allow us to transmit information to our JavaScript function.
Let’s modify our code:
+$( document ).ready(function() {
+ Shiny.addCustomMessageHandler('alert_title_and_text_under', function(arg) {
+ Swal.fire({
+ title: arg.title,
+ text: arg.text,
+ icon: arg.icon
+ });
+ })
+});
The notation to use here will be arg.parameter_name
to access the values transmitted by our {shiny}
application. The notation with the “.” is a JavaScript convention for accessing object properties. To make the parallel with R, it’s a bit like if we were doing arg$parameter_name
.
Our JavaScript code is ready! Let’s move back to the R side!
+{shiny}
app (FINALLY!)?We are going to add a button in the R/app_ui.R
file:
app_ui <- function(request) {
+ tagList(
+ # Leave this function for adding external resources
+ golem_add_external_resources(),
+ # Your application UI logic
+ fluidPage(
+ h1("golemsweetalert"),
+ actionButton(inputId = "show_alert",
+ label = "Alert demo")
+ )
+ )
+}
On the server side, we add an observeEvent()
in the R/app_server.R
file, which will call our JavaScript function to generate an alert when the user clicks on the “Alert demo” button.
Once more, {golem}
will make our life easier! We will use the golem::invoke_js()
function to call our JavaScript function.
Two parameters are passed to golem::invoke_js()
:
arg
object which will be used to transmit the information necessary to create the sweetalert2 alert. The names used in the list here must correspond to the names used in the JavaScript function for the arg
parameter (“title”, “text” and “icon”).app_server <- function(input, output, session) {
+ # Your application server logic
+ observeEvent(
+ input$show_alert,{
+ golem::invoke_js(
+ "alert_title_and_text_under",
+ list(
+ title = "Title",
+ text = "Text",
+ icon = "success"
+ ))
+ }
+ )
+}
We run a call to golem::run_dev()
to see our application in action!
Congratulations! 👏
+To make everything more elegant, we can create an R function that will call golem::invoke_js()
.
We are going to go through {golem}
to create our function. To do this, we will add the following line in the dev/02_dev.R
file of our application:
golem::add_fct("create_alert_title_and_text_under")
We obtain a R/fct_create_alert_title_and_text_under.R
file in which we will be able to write our function, which will call the JavaScript code created in the previous step.
#' create_alert_title_and_text_under
+#'
+#' @description Creates an alert with a title, a text and an icon
+#' @param title alert title
+#' @param text alert text
+#' @param icon alert icon
+#' @return side effect : creates an alert
+#'
+#' @noRd
+create_alert_title_and_text_under <- function(
+ title = "Title",
+ text = "Text",
+ icon = "success"
+ ) {
+ golem::invoke_js(
+ "alert_title_and_text_under",
+ list(
+ title = title,
+ text = text,
+ icon = icon
+ )
+ )
+}
Let’s modify both the R/app_ui.R
and R/app_server.R
files to be able to define the parameters of our alert through choices made by the user.
app_ui <- function(request) {
+ tagList(
+ # Leave this function for adding external resources
+ golem_add_external_resources(),
+ # Your application UI logic
+ fluidPage(
+ h1("golemsweetalert"),
+ textInput(inputId = "title",
+ label = "title"),
+ textInput(inputId = "text",
+ label = "text"),
+ radioButtons(inputId = "icon",
+ label = "icon",
+ choices = c("warning", "error", "success", "info", "question")),
+ actionButton(inputId = "show_alert",
+ label = "Alert demo")
+ )
+ )
+}
To define the list of possible “choices” for the radioButtons
, we took the possible values for the icon
parameter of sweetalert2, as indicated in the official documentation: https://sweetalert2.github.io/#icon.
app_server <- function(input, output, session) {
+ # Your application server logic
+ observeEvent(
+ input$show_alert,{
+ create_alert_title_and_text_under(
+ title = input$title,
+ text = input$text,
+ icon = input$icon
+ )
+ }
+ )
+}
Let’s finally call golem::run_dev()
!
Bravo ! 👏
+Following the previous steps, it is relatively easy to add additional elements to an alert, such as an image or confirmation / cancellation buttons. A slightly deeper dive into the sweetalert2 documentation will help you understand how to add these elements.
+Let’s see what is possible to achieve:
+You are curious to know more? Go to this project source code.
+{golem}
makes it easier for usIf you want more examples of the use of sweetalert2, but also other JavaScript libraries (Grid.js and Chart.js), you can consult the mycomicslibrary application and take a look at its source code.
+Thanks for reading this tutorial and have fun in the wonderful world of JavaScript! 🚀
+ + +