Delete/put links break your Rails app

In Rails you can easily use the link_to helper in your templates to create links that will generate HTTP POST, PUT and DELETE requests when clicked. You can do this using simply setting the :method option, but this ease of use hides a very real problem.

The problem

These links only work for users with javascript enabled.

Visitors without javascript enabled the links will actually take them to the show action of your controller (or a 404 if you con’t have a show action), which won’t be what you or they expect.

It’s easy not to notice this behaviour since mostly you test your app with javascript enabled.

For example:

link_to "Delete Image", @image, :method => :delete

Generates

<a href="/images/9" onclick="var f = document.createElement('form');
  f.style.display = 'none';
  this.parentNode.appendChild(f);
  f.method = 'POST';
  f.action = this.href;
  var m = document.createElement('input');
  m.setAttribute('type', 'hidden');
  m.setAttribute('name', '_method');
  m.setAttribute('value', 'delete');
  f.appendChild(m);f.submit();return false;">Delete Image</a>

Take note of all the javascript gymnastics to make this work.

The solution

The easy fix is to use the button_to helper instead.

For example:

button_to "Delete Image", @image, :method => :delete

Generates

<form method="post" action="/images/9" class="button-to">
  <input name="_method" type="hidden" value="put" />
  <input type="submit" value="Delete Image" />
</form>

A nice little submit button with no javascript and no pain.

This may even be a better user experience for all your users (with or without javascript) because the button will indicate to them that they are making a change (either updating or deleting something) rather than just visiting a link.

The solution (part II)

Of course perhaps you used the link_to helper because you wanted it to look like a standard link on the page (rather than a submit button).

One possible solution would be to use CSS but it’s actually very difficult to use CSS to make a submit button look exactly like (rather than just similar to) your other links across a range of browsers.

Another is to use javascript to replace the form generated by the button_to helper with a link. Then javascript enabled visitors will see a link just like if you had used the link_to helper, and visitors without javascript enabled will see a button.

If you are using jQuery then this can be achieved by adding the following javascript to your site.

jQuery.fn.convert_submit_button_to_link = function () {
  this.each( function () {
    var button = jQuery(this);
    button.
      hide().
      after(jQuery('&lt;a href="#">' + button.attr('value') + '&lt;/a>').
      attr('class', button.attr('class')).click(button.attr('onclick')));
  });
  return this;
};

$(document).ready(function () {
  // Convert any input with the class 'submit-link' to a link
  $('input.submit-link').convert_submit_button_to_link();
};

And then any submit button with the class ‘submit-link’ will be replaced by a link that will submit the form.

For example the submit button generated by the following would be converted to a link

button_to "Delete Image", @image, :method => :delete, :class => 'submit-link'

So there are no excuses for using link_to for destroy/update actions.

I will leave it as an exercise for the reader to convert this jQuery specific javascript to your favourite javascript library.