0

Hi I'm trying to execute an HTTP DELETE request using the sweet alert 2 library on Rails 6.1.3.1! When I try something like this it works perfectly see my other question about this issue:

<button script='
Swal.fire(
  "Good job!",
  "You clicked the button!",
  "success"
)
location.pathname = "<%= home_about_path %>"; //redirect to About Page
'>TEST</button>

But that would only work with GET request. Therefore it has been suggested that to achieve another type of request (i.e. DELETE), I would have to use AJAX. So, I found out that you can use Rails.ajax(...) to execute asynchronous JavaScript. But when I add the following code, I get the error: friends:22 Uncaught (in promise) ReferenceError: Rails is not defined.

//dir: project-name/app/views/friends/index.html.erb
Swal.fire(
  "Good job!",
  "You clicked the button!",
  "success"
)
Rails.ajax({
  type: "GET",
  url: "<%= home_about_path %>",
  success: function(response){ alert("succes Go to About Page") },
  error: function(response){ alert("error :" + response) }
});

Therefore I tested Rails.ajax(..) inside project-name/app/javascript/packs/application.js where the declaration of the Rails is made! Of course the following code will result in an infinite loop. But this was done only for testing purposes! And it works fine. The page redirects to about page again and again and again.

import Rails from "@rails/ujs"
import Turbolinks from "turbolinks"
import * as ActiveStorage from "@rails/activestorage"
import "channels"

Rails.start()
Turbolinks.start()
ActiveStorage.start()

Rails.ajax({
  type: 'GET',
  url: '/home/about',
  success: function(response){ location.pathname = "/home/about" },
  error: function(response){ alert('error delete' + response) }
});

So my question is why the command Rails.ajax() will work where it is declared, but not on a specific page/controller? How to tell rails to declare the Rails var to the whole system?


SOLUTION by @Joel_Blum:

application.js

import Rails from "@rails/ujs"
import Turbolinks from "turbolinks"
import * as ActiveStorage from "@rails/activestorage"
import "channels"

Rails.start()
Turbolinks.start()
ActiveStorage.start()

window.Rails = Rails;

index.html.erb:

Swal.fire(
  "Good job!",
  "You clicked the button!",
  "success"
)

var redirect = '<%= friends_path %>';
redirect += '/<%= friend.id %>';

Rails.ajax({
  type: 'DELETE',
  url: redirect,
  success: function(response){ 
    //Friend has been deleted!
  },
  error: function(response){ 
    //Error with Ajax
    alert('error delete' + response) 
  }
});
  • Its because the javascript you put inside your view (called inline javascript) is not being compiled by webpacker / assets pipeline, it has no knowledge of the other javascript in your system. – Joel Blum May 10 '21 at 12:02
  • Okay I understand but is there a way to connect this. Since `Rails` is declared somewhere in the system, how to let inline JavaScript know where to find that name? – programming_ritual May 10 '21 at 12:04
  • Keep in mind that javascript inside your views can't use es6 syntax or imports, in general it's best to not do this a lot. You could put all your javascript in application.js and simply register a click event handler and fire the request there. – Joel Blum May 10 '21 at 12:20
  • Is that what @max suggested? – programming_ritual May 10 '21 at 12:22
  • Yes. You can also use the Rails UJS handlers with other request types like json by using `data-type="JSON"` – max May 10 '21 at 12:44
  • Hi Max, can you go on a private chat? I'm not sure I follow your answer – programming_ritual May 10 '21 at 12:48

2 Answers2

1

There are several ways to deal with this. What you are asking is basically how to make the Rails object global without having to use module imports.

import Rails from "@rails/ujs";
window.Rails = Rails;

Should take care of it or through webpack as described in this answer

Joel Blum
  • 7,750
  • 10
  • 41
  • 60
0

To send a DELETE request to a Rails app you would send:

  • A request with the HTTP method DELETE. Modern browsers can do this when sending XHR (ajax) requests.
  • Or a POST request with the parameter _method=DELETE.

If you want to fire a ajax call when the user clicks an element you want to setup an event handler. This should be done in your application.js and not inline:

document.addEventListener('click', function(e){
  if (!e.target.matches('.my_special_button')) {
    return false;
  }
  Rails.ajax({
    type: 'DELETE',
    url: '/home/about',
    success: function(response){ location.pathname = "/home/about" },
    error: function(response){ alert('error delete' + response) }
  });
});

Your code will just fire the ajax request as soon as the file is loaded.

Rails UJS lets you send Ajax requests by just adding data properties to the button itself.

<button data-remote="true" data-method="DELETE" data-url="/home/about">TEST</button>

You can then use a js.erb template which evaled and fired when the ajax request finishes:

# app/views/pages/about.js.erb
Swal.fire(
  "Good job!",
  "You clicked the button!",
  "success"
)
location.pathname = "<%= home_about_path %>"; //redirect to About Page

Rails UJS defaults to sending ajax request with the Application/Javascript mime type.

Or use antother type like JSON and listen for the Rails UJS ajax events:

<button data-remote="true" data-method="DELETE" data-url="/home/about" class="my_special_button">TEST</button>
document.addEventListener("ajax:success", (event) => {
   const [data, status, xhr] = event.detail;
   if (!e.target.matches('.my_special_button')) {
     return false;
   }
   Swal.fire(
     "Good job!",
     "You clicked the button!",
     "success"
   )
   location.pathname = "/home/about";
});
max
  • 96,212
  • 14
  • 104
  • 165
  • Yes, you can use globals and inline JS but you're really just making a mess and delaying your own progress as a dev. – max May 10 '21 at 12:22