views:

1665

answers:

4

I'm putting together PUT/DELETE links, a la Rails, which when clicked create a POST form with an hidden input labelled _method that sends the intended request type. I want to make it DRYer, but my jQuery knowledge isn't up to it.

HTML:

<a href="/articles/1" class="delete">Destroy Article 1</a>
<a href="/articles/1/publish" class="put">Publish Article 1</a>

jQuery:

$(document).ready(function() {

  $('.delete').click(function() {
    if(confirm('Are you sure?')) {
      var f = document.createElement('form');
      $(this).after($(f).attr({
        method: 'post',
        action: $(this).attr('href')
      }).append('<input type="hidden" name="_method" value="DELETE" />'));
      $(f).submit();
    }
    return false;
  });

  $('.put').click(function() {
    var f = document.createElement('form');
    $(this).after($(f).attr({
      method: 'post',
      action: $(this).attr('href')
    }).append('<input type="hidden" name="_method" value="PUT" />'));
    $(f).submit();
    return false;
  });

});
+2  A: 

Okay, so I finally tested this. It does what it claims on the box.

$(document).ready(function() {

  $.fn.getClassInList = function(list){
       for(var i = 0; i < list.length; i++)
       {
          if($(this).hasClass(list[i]))
             return list[i];
       }
       return "";
  }

  $('.delete,.put').click(function() {
    if(confirm('Are you sure?')) {
      var f = document.createElement('form');
      $(this).after($(f).attr({
        method: 'post',
        action: $(this).attr('href')
      }).append('<input type="hidden" name="_method" value="' 
            + $(this).getClassInList(['put', 'delete']).toUpperCase() 
            + '" />'));
      $(f).submit();
    }
    return false;
  });

});
Stobor
Although, you do limit the ability to style it with other classes, then.
Stobor
Agreed with Stobor. Thanks, though.
Aupajo
Okay, so I fixed it so that it works with multiple classes, so it can be styled normally...
Stobor
I struggled with the same problem for AJAX. Script change will be: $.post(this.href, { _method: 'delete' }, null, "script");
Vitaly
A: 

Forget about your hidden field and just change the method to put or delete. I would select the form instead of creating it then just change the attributes

$("form").attr("method", "put").submit()

Martin Murphy
Won't that only work if the browser supports PUT/DELETE requests though?
Aupajo
Well yes but that wasn't a requirements and other than handling it on the server side which wouldn't be true REST in the first place then there isn't a way around that.
Martin Murphy
I'm using the _method field as a workaround. The server will interpret it as a pseudo-PUT request.
Aupajo
soitgoes: HTML4 forms do not support PUT or DELETE, so your answer is incorrect and violates standardized specifications.
Wahnfrieden
in either case I don't see the reason to create an additional form element in the DOM. My answer doesn't "violate" any standards, I didn't claim it to be standard compliant to html4. Simply appending/replacing the hidden field would suffice to the existing form.
Martin Murphy
Why would you suggest something which is not only unsupported by browsers, but is also a violation of standards? The most recent HTML standard is HTML 4.1, so you are definitely in violation.
Wahnfrieden
Now you're just flaming. Get a life.
Martin Murphy
The reason for the extra DOM node is that the alternative of adding a query parameter prevents caching. Your method is simply incorrect and broken on any standards-compliant browser. I don't see why pointing this out, then explaining myself when you disagree, is flaming.
Wahnfrieden
I'm talking about the additional form element. I see the reason for the additional hidden input tag. I don't see the reason for the additional form element.
Martin Murphy
+4  A: 

You can just use forms instead of links and let the jQuery form plugin handle the submission if you want to use ajax:

<form class="delete" method="post" action="/articles/1">
    <input type="hidden" name="_method" value="delete"/>
    <button type="submit">Delete</button>
</form>

jQuery:

$(document).ready(function() {
    $('form.delete').ajaxForm(options);
});

The options variable can contain pre and post-submit callbacks.

Style your buttons like links if that's what you need.

rojoca
Hmm, thanks, but I don't like the additional markup. Just after what I have above, but DRYer.
Aupajo
Doing it this way: follows the DRY mantra as succinctly as possible, is semantically correct, is the way most forms on the web are made ie KISS (extra acronym goodness), and makes use of progressive enhancement (works without javascript). All that for a little extra markup.
rojoca
Don't that button have to be an input type="submit" or something for that form to work without javascript?
Svish
@Svish - yes. (updated answer)
rojoca
A: 

I just tested the following and it worked for me:

$(function() {
  $('.post, .POST').click(function() {
    $('<form method="POST" style="display:none"><input type="hidden" name="_method" value="POST" /></form>')
      .insertAfter($(this))
      .attr({
        action: $(this).attr('href')
      }).submit();
    return false;
  });
  $('.put, .PUT').click(function() {
    $('<form method="POST" style="display:none"><input type="hidden" name="_method" value="PUT" /></form>')
      .insertAfter($(this))
      .attr({
        action: $(this).attr('href')
      }).submit();
    return false;
  });
  $('.delete, .DELETE').click(function() {
    if(confirm('Are you sure?')) {
      $('<form method="POST" style="display:none"><input type="hidden" name="_method" value="DELETE" /></form>')
        .insertAfter($(this))
        .attr({
          action: $(this).attr('href')
        }).submit();
    }
    return false;
  });
});

What kind of problems where you having?

Joe Beda
Just trying to make it DRYer.
Aupajo
How many times did you R yourslf on this post?
Martin Murphy
I had misunderstood the original question and thought the posted code didn't work. Instead, the original poster was looking to simplify the implementation. I wouldn't call that making it DRYer, but instead just simpler. You are still only specifying the important stuff (the link should be a PUT) only once.DRY (Don't Repeat Yourself) is primarily about single truth in configuration information. You trade a little bit of complex framework code so that you only have to specify configuration information once. In this way the complexity of the handlers I have here doesn't matter that much.
Joe Beda