Replace Confirm Dialog Box with Sweet Alert in Ruby on Rails 6

In this post, we will discuss how we can integrate the Sweet Alert 2 library into a Rails 6 application. We will build a simple to-do app, and will replace the default confirm dialog box triggered by links such as delete links, with a sweet alert. We will use Rails 6.1.4.1 and Ruby 3.0.3.

Set Up the Application

We will generate a new application with the --skip-turbolinks option. We’ll call our application TodoApp. From the terminal we type:

rails new ToDoApp --skip-turbolinks

Once the application is created, we change the directory to its folder:

cd ToDoApp

Install and Import Sweet Alert 2

We will install the Yarn package, sweetalert2. In the terminal, we type:

yarn add sweetalert2

We need to import the JavaScript code and the stylesheet into our application.

In the application.js pack in the app/javascript/packs/ folder,  we import the minified  JavaScript file:

import Swal from 'sweetalert2/dist/sweetalert2.min.js'
window.Swal = Swal

We will import the stylesheet in the application.css file in the app/assets/stylesheets/ folder:

@import 'sweetalert2/dist/sweetalert2.min.css'

Generate a TASK Scaffold

Our app is simple, we have only a Task resource.

The Task model has only one attribute: the name. Let’s generate the appropriate scaffold in the terminal:

bin/rails g scaffold Task name:string

And then we run:

bin/rails db:migrate

We also delete the scaffolds.scss file that was generated automatically in the app/assets/stylesheets/ directory, because we don’t need it.

Set a Root Route

Our application requires a root route, so let’s set it to the Tasks controller’s index action. In config/routes.rb add this at the top:

root 'tasks#index'

Edit the delete links in index.html.erb

Now, it’s time to replace the default confirm box with sweet alerts. In our index.html.erb file in the app/views/tasks/ folder, we have delete links that, when clicked, trigger a confirm dialog box. We will remove the data-confirm attribute, then we will add a :remote option to make an AJAX request, and finally, we will write the code to fire a sweet alert when these links are clicked.

We also add a ‘delete-links’ class to these links, so we can easily select them via JavaScript.
Let’s open the index.html.erb file and edit the delete links as explained above:

<%= link_to 'Delete', task, method: :delete, remote: 'true', class: 'delete-links' %>

I also changed the word “Destroy” to “Delete”.

Write the JavaScript Code

In Rails, usually, we put our custom JavaScript files in a subdirectory that we create in the app/javascript/ folder. Let’s call this subdirectory js and add a file called index.js to it. In this file, we’ll write the necessary code to make Sweet Alert 2 work with Rails. Finally, we import this subfolder into the application.js pack, so that this code can be compiled by Webpack:

import 'js'

Also, in the app/views/layouts/application.html.erb file, we’ll move the javascript_pack_tag from the <head> of the document right before the closing </body> tag.

Rails uses an unobtrusive scripting adapter (UJS) that provides helpers to assist in writing JavaScript code. Seems that when used with Webpack, window.Rails is undefined. So before we write our code, we must edit our application.js in order to define it.
Just add this line after the import statements:

window.Rails = Rails

Now, we open the index.js file and write the following code:

document.addEventListener('ajax:beforeSend', (event) => {
  if (event.target && event.target.className === 'delete-links') {
    const taskURL = event.target.getAttribute('href')
    Swal.fire({
      title: "Are you sure?",
      showCancelButton: true
    })
      .then(event.preventDefault())
      .then((result) => {
        if (result.isConfirmed) {
          Rails.ajax({
            type: "DELETE",
            url: taskURL
          })
        }
      })
      .catch(event.preventDefault())
  }
})

We attach an event listener to the custom UJS ajax:beforeSend event. But we do not attach this event listener directly to these links, but instead,  attach it to the document itself, using a technique called event delegation. First, we grab the task URL and store it in a variable called taskURL. Then we present the user with a sweet alert. The alert has a cancel button and a confirm button. After the sweet alert pops, we stop the execution of the Ajax request, waiting for the user to respond. And if the user confirms they want to delete the task, we delete it by making an AJAX delete request to the task URL that we stored in the taskURL variable.

Finally, I want to reload the page after the user deletes a task. In the app/views/tasks/ folder, we create the destroy.js.erb file and add this code:

window.location.reload()

And in the Tasks controller, we add the format.js in the respond_to block inside the destroy method:

respond_to do |format|
  format.js
  format.html { redirect_to tasks_url, notice: "Task was successfully destroyed." }
  format.json { head :no_content }
end

The controller will respond to the AJAX request and reload the page to show the updated list of tasks.

And that's it. From now on, when the user wants to delete a task, a stylish sweet alert pop-up will open instead of the default browser alert.

Post last updated on May 13, 2023