简体   繁体   中英

How do I let someone chain callback methods on my jQuery plugin?

I am wanting to to provide some extra functionality to jQuery.post(). I want to be able to check the response from the server and call different callbacks.

For example:

$("#frmFoo").postForm("ajax")
    .start(function () { showSpinner(); })
    .success(function () { alert ("Foo saved"); })
    .error(function() { alert ("Foo could not be saved at this time"); })
    .validationError(function() { alert("Please fix foo and try again"); })
    .complete(function() { hideSpinner(); });

So I want to be able to send back a consistent JSON object back to the page and based on that JSON object either call success or validationError providing additional info to validationError.

UPDATE It has been a steep learning curve and I am almost 100% sure that I have done something wrong. Below is my attempt at the functionality that I wanted.

(function ($) {
     $.fn.postForm = function (url) {
         var options = options || {};
         var formData = this.serialize();
         var validationCb = jQuery.Callbacks("once memory");
         var successCb = jQuery.Callbacks("once memory");
         var errorCb = jQuery.Callbacks("once memory");
         var completeCb = jQuery.Callbacks("once memory");
         var s = jQuery.ajaxSetup({ }, options);
         var callbackContext = s.context || s;
         foo = {
             success: function () {
                 successCb.add(arguments[0]);
                 return this;
            },
             complete: function() {
                 completeCb.add(arguments[0]);
                 return this;
            },
            validationError : function() {
                validationCb.add(arguments[0]);
                return this;
            },
            error: function() {
                errorCb.add(arguments[0]);
                return this;
            }
         };
         $.post(url, formData)
            .success(function(data, textStatus, jqXhr) {
                 if (!data.Success) {
                     validationCb.fireWith(callbackContext, [data.Message, data.KeyValuePairs]);
                 } else {
                     successCb.fireWith(callbackContext, [data, textStatus, jqXhr]);
                 }
            })
            .complete(function (jqXhr, textStatus) {
                completeCb.fireWith(callbackContext, [jqXhr, textStatus]);
            })
            .error(function(jqXhr, textStatus, errorThrown) {
                errorCb.fireWith(callbackContext, [jqXhr, textStatus, errorThrown]);
            });
         return foo;
     };
 })(jQuery);

So how bad is what I have done? I am not familiar with javascript and jQuery and I am sure that I am breaking some rules that is bound to bite me.

It's not very practical to do what you're proposing because it effectively changes the promise interface.

Note that much of what you want does already exist if you just return the result of $.post to the caller:

showSpinner();
$.post({...})
    .done(...)           // aka .success (deprecated)
    .fail(...)           // aka .error (deprecated)
    .always(stopSpinner) // aka .complete (deprecated)

So, you have to show the spinner first, but that's barely any hardship, and there's no way to catch the validation error, so that would need to be part of your .done function.

If i understand correct what you want to achieve, you should use $.ajax() .

$.ajax({
  url: your_url,
  type: 'POST'
  beforeSend: function(){ showSpinner(); },
  error: function() { alert ("Foo could not be saved at this time"); }
  success: function (data) { 
     if (data.validation == true) { //example of validation, depends on how you send data from backend
       alert ("Foo saved");
       hideSpinner();
     } else {
        alert("Please fix " + data.foo + " and try again"); //again depends on how you send data     
     }
  }

You are looking for $.Deferred (or $.Callbacks if you want something really custom), or more generally the promise pattern . A simple implementation might look something like this:

(function ($) {
    $.fn.postForm = function (url, options) {
        var options = options || {},
            s = jQuery.ajaxSetup({ }, options),
            callbackContext = s.context || s;

        var formData = this.serialize();
        var post = $.post(url, formData);
        var deferred = post.pipe(function(data, textStatus, jqXhr) {
            if (!data.Success) {
                // HTTP OK, soft error
                return $.Deferred().rejectWith(callbackContext, [data.Message, data.KeyValuePairs]);
            } else {
                // success
                return $.Deferred().resolveWith(callbackContext, [data, textStatus, jqXhr]);
            }
        }, function(jqXhr, textStatus, errorThrown) {
            // HTTP error
            return $.Deferred().rejectWith(callbackContext, [errorThrown, {}]);
        });

        deferred.success = deferred.done;
        deferred.error = deferred.fail;
        deferred.complete = deferred.always;

        return deferred;
    };
})(jQuery);

This does not differentiate between error / validationError , but gets rid of most of the biolerplate code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM