views:

666

answers:

4

Is it possible to automate the showing/hiding of a ajax loading gif, and the disabling/enabling of the submit button at the same time? (when the submit button is a styled < a > not a input type=submit)

Currently when submitting I do this:

$("#save_button_id").click(function () {
        if ($('#save_button_id').hasClass('ui-state-disabled')) return false;
        Save();
});

function Save() {
StartAjax($("#save_button_id"));
$.ajax({
        success: function (data) {
            EndAjax($("#save_button_id"));
            // etc...
        },
        error: function (xhr, status, error) {
            EndAjax($("#save_button_id"));
            // etc ...
        }
});
}

function StartAjax(button) {
    DisableButtonClick(button);
    $("#ajaxLoad").show();
}

function EndAjax(button) {
    EnableButtonClick(button);
    $("#ajaxLoad").hide();
}

I've seen a few places talk about how to use .ajaxStart() to automatically show the loading gif, but is it possible to also find a reference to the button (a styled < a > tag) that was clicked, and automatically disable/enable that as well?

The point of this is to not have to manually type in Start/EndAjax every time, and to make sure the app is consistent everywhere.

EDIT

None of the answers so far offer automation - any solution (like my current one above) where you have to manually type start/end before and after every single $.ajax() causes a maintenance problem: Its easy to forget to place the start/end next to some $.ajax() calls, and if you want to change how it works later you need to go through every single one to make the change.

EDIT 2 - to clarify point re the .delegate() suggestion

You said "You can attach your event handler to any element" - but I want to attach it to every button element (DRY!): so I've modified the 1st part of your suggestion to be:

$('div#buttons a.ui-icon').each(function(index) {
    $(this).ajaxStart(function() {
        $("#ajaxLoad").show();
    });
});

This solves the first problem, which is how to show the loading .gif for any button, without having to repetitively type "$("#ajaxLoad").show()" everywhere there is an $.ajax() call.

The next part is how to disable any button when it is clicked (again with no repetitive code) - you suggested .delegate(). But in your example every button click will call the Save() method. (I changed the selector to match my html)

$('div#buttons a.ui-icon').delegate('button:not(.ui-state-disabled)', 'click', function() {
   $(this).toggleClass('ui-state-disabled');
   Save();  // this line needs replacing with a call to $(this).something
});

The problem with this is that $(this) is not always the save button (the selector returns ALL the buttons), it might be the delete button or the cancel button or etc. So calling $(this).toggleClass is fine, but calling Save() means you are calling the wrong function. All these buttons already have a .click method:

$("#SaveButton").click(function () {
    Save();
});

$("#DeleteButton").click(function () {
    Delete();
});

So this is the original click function that needs to be called where it says $(this).something above. At this point it should be calling the original click - or perhaps it is more correct to say it should then bubble up to the original .click. The .delegate must be more generic, and the original .click will provide the specific implementation.

A: 
$("#save_button_id").click(function () {
  if ($('#save_button_id').hasClass('ui-state-disabled')) {
    return false;
  } else {
    // add disabled class
    $('#save_button_id').addClass('ui-state-disabled');

    // optionally disable the button if it's an input button
    $('#save_button_id').attr('disabled','disabled');

    // show ajax loader
    $("#ajaxLoad").show();

    $.ajax({
      url: 'ajax/stuff.html',
      success: function(data) {
        // do stuff

        // re-enable button
        $('#save_button_id').removeClass('ui-state-disabled');

        // optionally re-enable the button if it's an input button
        $('#save_button_id').attr('disabled','');

        // hide ajax loader
        $("#ajaxLoad").hide();
      },
      error: function (xhr, status, error) {
        // do stuff

        // re-enable button
        $('#save_button_id').removeClass('ui-state-disabled');

        // optionally re-enable the button if it's an input button
        $('#save_button_id').attr('disabled','');

        // hide ajax loader
        $("#ajaxLoad").hide();
      });
  }

  // prevent default action, if any
  return false;
});

Edit: and to put those in a function:


function processing(status) {
  if (status == 1) {
    // add disabled class
    $('#save_button_id').addClass('ui-state-disabled');

    // optionally disable the button if it's an input button
    $('#save_button_id').attr('disabled','disabled');

    // show ajax loader
    $("#ajaxLoad").show();
  } else {
    // re-enable button
    $('#save_button_id').removeClass('ui-state-disabled');

    // optionally re-enable the button if it's an input button
    $('#save_button_id').attr('disabled','');

    // hide ajax loader
    $("#ajaxLoad").hide();
  }
}

And then call on that function:

$("#save_button_id").click(function () {
  if ($('#save_button_id').hasClass('ui-state-disabled')) {
    return false;
  } else {
    processing(1);

    $.ajax({
      url: 'ajax/stuff.html',
      success: function(data) {
        // do stuff

        processing(0);
      },
      error: function (xhr, status, error) {
        // do stuff

        processing(0);
      });
  }

  // prevent default action, if any
  return false;
});
Alec
+2  A: 

You can attach your event handler to any element; so for your save button:

$(this).ajaxStart(function() {
  $("#ajaxLoad").show();
});

Now, you can listen for the click event for each button on your page and call the ajax function from that element:

$('div').delegate('button:not(.ui-state-disabled)', 'click', function() {
   $(this).toggleClass('ui-state-disabled');
   Save();
});

The $.delegate() function listens to click events for each element that does not have the .ui-state-disabled class. When an event is fired, it will toggle the class of that button to disabled, call your Save function and the $.ajaxStart() event will fire. That event will show your animated ajax gif.

I have assumed that your buttons are all contained in a div for this example. You many want to modify the code so that $('div').delegate... actually specifies the containing element of your buttons. I am also assuming you're using jQuery 1.4.2.

You should be able to use the $.ajaxStop() function to revert everything back again.

Phil.Wheeler
That looks good. Is it possible to call the buttons original click function, instead of hard coding Save()? because of course only 1 button calls Save(), other buttons call Delete(), DoSomethingElse() etc (and thats a yes to using 1.4.2)
JK
Can you provide more explanation? From where do you want to call the button click event?
Phil.Wheeler
I mean that, where you have "Save()" in your delegate example is it possible to replace it with a call to the selected object's current click function. eg $(this).call_original_click_function(); - I dont know what the syntax might be. (btw as I understand it .delegate() will replace the current .click with a new one - is that correct)
JK
Sorry - what do you actually want to do in that function once you have the handle to the original click event (which you already have in the delegate method, btw)?
Phil.Wheeler
Edited the post in response to comment - so I can use syntax highlighting etc.
JK
Ah! I see what you're doing. Well, the easiest (if not the most elegant) approach might be to use the *name* attribute for each button. So the save buttons would have name="Save", deletes would have name="Delete" and so on. Then you'd just use a switch statement in your code to determine which function to call based on the value of that attribute.
Phil.Wheeler
+3  A: 

It actually turned out to be very straightforward: I've included this in my helper.js file that is run for every page:

$(document).ready(function () {
    $('div#buttons a.fm-button').each(function (index) {
        $(this).ajaxStart(function () {
            $("#ajaxLoad").show();
            DisableButtonClick($(this));
        });
        $(this).ajaxStop(function () {
            $("#ajaxLoad").hide();
            EnableButtonClick($(this));
        });
    });
});

Now whenever any of the ajax-y buttons are clicked on any page, the buttons are disabled and the ajax loading gif is shown. When the ajax call ends they are returned to their normal state. And all done with out repetitively typing code every time .ajax() is called.

JK
A: 

thanks , nice post

משחקים