Daniel
Daniel

Reputation: 45

Sinatra: Routes that do not route

My Sinatra webapp has a list of articles which can be deleted by the users. I've added a jQuery animation to make the deleted articles fade away before the actual (DB) deletion is carried out by a Sinatra route. The code is elementary:

# script.js
$(".lnk-delete").click(function()
  {
    var id = $(this).closest("article").attr("id");
    $("#"+id).fadeToggle(1000);
  });

# app.rb
get '/delete/articles/:id' do
    Articles.get(params[:id]).destroy
    redirect to('/')
end

The problem is, the code reroutes to the same page, which is now unnecessary since the deleted article is no longer showing. Not only the reroute generates unnecessary load, it also brings the user back to the top of the page (making the experience confusing and annoying).

I have looked around, tried returning HTTP codes, tried halt(ing), tried the request.xhr function and anything else I could find online, all to no avail...

Is there a way to make the route "not route", i.e. execute the db.destroy and leave the actual page "unrefreshed"?

Update

The following code, a slightly modified version of @exobrain's answer, solved my problem.

# script.js
$(".lnk-delete").click(function(clicked) {
    clicked.preventDefault();
    var id = $(this).closest("article").attr("id"); $("#"+id).fadeToggle(1000); 
    $.get($(this).children("a").attr("href"));
});

Three things changed in my original code:

  1. Added preventDefault to the script so as to prevent the link being followed;
  2. The link request is now done by jQuery, using get;
  3. The Sinatra route was modified so it no longer redirects to '/'.

Here's the code:

# app.rb
get '/delete/articles/:id' do
    Articles.get(params[:id]).destroy
end

As a note to beginners (as myself), this get route is inconsistent with best practices. It should be a delete route. I'll fix that, eventually.

Thanks, @exobrain & @Amadan, for the answers.


P.S.:

Fixed the get route. All it took was changing

$.get($(this).children("a").attr("href"));

to

$.ajax({ url: $(this).children("a").attr("href"), type: "delete"});

and then get '/delete/articles/:id' do to delete '/delete/articles/:id' do on the actual Sinatra application.

Upvotes: 1

Views: 249

Answers (3)

ian
ian

Reputation: 12251

Instead of a redirect, use a halt at the end of the route with the correct status code. You should also get rid of that GET as you've been advised.

get '/delete/articles/:id' do
    Articles.get(params[:id]).destroy
    redirect to('/')
end

becomes

get '/articles/:id' do
  # get the article here
end

delete '/articles/:id' do
  Articles.get(params[:id]).destroy
  halt 200
  # I'd also return a representation of the deleted article
  # or perhaps it's ID
  # but maybe that's just me
end

HTTP has verbs for a reason. Don't use $.get, use $.ajax and pass it the correct verb i.e. DELETE. See Type in the link.

Notice that the verb no longer needs to be in the URL as well.

Upvotes: 0

comradelion
comradelion

Reputation: 133

It's a bit odd to be using a get request to delete a resource, but none-the-less, you can do this using ajax. I'm assuming .link-delete is an a tag, and has a href attribute pointing to the Sinatra endpoint. The following code will stop the link from being followed through by the browser (preventDefault), then perform an AJAX get request to the url pointed to by the link. When the request completes and returns, the fadeout is performed in the callback.

$(".lnk-delete").click(function(e) {
  e.preventDefault();
  $.get(this.href, function() {
    var id = $(this).closest("article").attr("id");
    $("#"+id).fadeToggle(1000);
  }
});

Upvotes: 0

Amadan
Amadan

Reputation: 198408

If you're submitting to /delete/articles/... via AJAX, the actual page will remain unrefreshed. (You have not shown the submit code.) You can simply return "" from your controller instead of the redirect for a blank body.

If you're not using AJAX, then the fadeout is superfluous; you will need that redirect.

Upvotes: 1

Related Questions