s

Posts Tagged with "jquery"

Rails DELETE requests with JQuery

May 20, 2009  -  Comments

When I write admin controls for for a Rails app, I like to make the delete links just fire off an Ajax request, delete the record, and remove the element from the page. This is easy with the Rails default setup using Prototype. All you have to do is use link_to_remote with a :method => 'delete' to make Rails do all the work. The problem is, this is the code that Rails generates:

<%= link_to_remote("Delete", :url => admin_post_path(post), :method => :delete) %>
<!- turns into: -->
<a href="#" onclick="new Ajax.Request('/users/63', {asynchronous:true, evalScripts:true, method:'delete'});); return false;">Delete</a>

While this isn't terrible, it's certainly not very pretty. Surely we can make the code cleaner and simpler!

Well, we've recently switched to using JQuery for all of our new apps at Plexus. Unless you use the JRails plugin, the link_to_remote helper no longer works when using JQuery. I figured this would be a good time to switch the remote calls to be handled separately by JavaScript, instead of spitting out a ton of script in the HTML.

It's easy to just hijack links via JavaScript and have them submit via Ajax, but how do we handle REST requests that require the DELETE method?

I decided to just add a specific class to all links that I wanted to be hijacked by JavaScript, then add the code in my js file to handle the links with Ajax. My DELETE links would look like this:

<!- Link that uses DELETE method -->
<%= link_to 'Delete', entry_path(entry), :class => 'remote-delete' %>

The key to hijacking the link is to include the hidden _method attribute set to 'delete'. My JavaScript to hijack the link would look like this:

$(document).ready(function() {
  $('a.remote-delete').click(function() {
    // we just need to add the key/value pair for the DELETE method
    // as the second argument to the JQuery $.post() call
    $.post(this.href, { _method: 'delete' }, null, "script");
    return false;
  });
});

This converts every link that has the remote-delete class to an Ajax request with the DELETE method!

Tagged: jquerydeleterest

Emulating RJS with Merb/JQuery/Haml

Dec 21, 2008  -  Comments

Ok, Merb is great. Rails is equally as great. RJS is great. JQuery and Haml are just outstanding. But I found that Merb not doing RJS-like behavior a little daunting in the current application that I'm working on. Of course, Merb does javascript, and you can send Ajax requests, but it's not quite as easy to accomplish RJS-type interactions with the current page. I did quite a bit of googling, and all I ever came up with was a very interesting presentation from Yehuda Katz about using JQuery with Merb. It didn't completely answer my question about getting RJS functionality in a Merb app. So, I set about trying to actually figure something out on my own!

Let's say I have an action called mark_as_complete that I can call for several items on a page, have it make a database call, then update the item without having to leave the page. The first step is to make the form for each item submit via AJAX instead of a regular HTTP POST request. Thanks to Ryan Bates' RailsCast on JQuery for help on that. All I have to do is give the form a class of remote, then this function will cause it to be submitted via AJAX (as well as any form on the site with the same class name):

// function to send the jQuery form object via AJAX
jQuery.fn.submitWithAjax = function() {
  this.submit(function() {
    // be sure to add the '/posts/taggedwith/jquery/.js' part so that it knows this is format:js
    $.post(this.action + '/posts/taggedwith/jquery/.js', $(this).serialize(), null, "script");
    return false;
  })
  return this;
};

$(document).ready(function() {
  // once the page has completely loaded,
  // make forms with the class "remote" submit via AJAX
  $("form.remote").submitWithAjax();
});

Now that's done, I need to figure out how to get the RJS stuff to fire correctly. I've already set the format of the request as .js, so the controller knows it's an AJAX request. With Merb, you have to add the provides :js line to your action so that it knows to accept js requests.

def mark_as_complete(id)
  provides :js
  # do work, son
  # set instance variable to indicate success/failure
  if stuff_done
    @complete = true
  end
  render
end

Now for the funky part. Getting my Haml template to update the page. Well, if you just name a view mark_as_complete.js.haml, then the application will use it. The hard part was figuring out what to do in the file. I could call javascript in the file and get that to work (alert(), for example), but the following bit of code did NOT work:

- if @complete
  $("#div_name_#{@record.id}").html('complete');
  $("#notes_#{@record.id}").replaceWith('

#{@record.notes}

'); - else alert("An error occurred processing your request.");

Nothing happened and I couldn't figure out why. The javascript was being run, but the ruby code inside it wasn't being evaluated. Turns out it's because I'm using Haml. I suspect that if I were using erb, then I could surround my ruby code with output blocks(<%= %>) and it would work(though I haven't tested it).

After experimenting with all sorts of variations on the above code, I finally decided to try and put the javascript code into Haml ruby output blocks(so the ruby code would be evaluated). Low and behold, the following actually worked, despite being ugly and less readable.

- if @complete
  = "$('#div_name_#{@record.id}').html('complete');"
  = "$('#notes_#{@record.id}').replaceWith('

#{@record.notes}

');" - else alert("An error occurred processing your request.");

Notice that the alert() call isn't in an output block because it doesn't have any ruby code that needs to be evaluated.

This solution worked for me, but I'm sure there is a better way to accomplish this. If anyone has a better approach, please leave it in the comments.

Tagged: rubyhamljquerymerbrjstutorial