简体   繁体   中英

Calling a function on a function - Jquery

I have coded a javascript file:

$(function() {
  return $(".ajax-form").on("ajax:success", function(e, data, status, xhr) {
    var model_name;
    model_name = $(this).data('model-name');
    console.log('ajax form success');
    if (model_name === 'contact') {
      return $('#modal-alert-contact').modal('show');
    } else {
      return $('#modal-alert-demo').modal('show');
    }
  }).bind("ajax:error", function(e, xhr, status, error) {
    var elm, messages, model_name;
    model_name = $(this).data('model-name');
    console.log('ajax form error');
    console.log(model_name);
    if (model_name === 'contact') {
      if (xhr.responseJSON["email"]) {
        elm = $('.alert-contact-fields');
        messages = [];
        $.each(xhr.responseJSON, function(id, error_messages) {
          return messages.push(("<li><strong class='titleize'>" + id + "</strong> - can't be blank</li>").replace(/_/g, " "));
        });
        elm.find('.messages').html(messages);
        return elm.removeClass('hide');
      } else {
        elm = $('.alert-contact-fields');
        return elm.addClass('hide');
      }
    } else {
      if (xhr.responseJSON["company_name"]) {
        elm = $('.alert-demo-fields');
        messages = [];
        $.each(xhr.responseJSON, function(id, error_messages) {
          return messages.push(("<li><strong class='titleize'>" + id + "</strong> - can't be blank</li>").replace(/_/g, " "));
        });
        elm.find('.messages').html(messages);
        return elm.removeClass('hide');
      } else {
        elm = $('.alert-demo-fields');
        return elm.addClass('hide');
      }
    }
  });
});

and I found it out messy, and repeating same codes. What I'm want to do is this part:

        messages = [];
        $.each(xhr.responseJSON, function(id, error_messages) {
          return messages.push(("<li><strong class='titleize'>" + id + "</strong> - can't be blank</li>").replace(/_/g, " "));
        });
        elm.find('.messages').html(messages);
        return elm.removeClass('hide');

I want that part to be a function, and after I do that, I will call that function to use it on my function. Is it possible or there's some technique to improve my coding structure?

Thanks!

I think you want something like this:

$(function() {
    var myform = $(".ajax-form");

    var makeMessages = function(json) {
        return $.map(json, function(error_messages, id) {
            return ("<li><strong class='titleize'>" + id + "</strong> - can't be blank</li>").replace(/_/g, " ");
        });
    };

    myform.on('ajax:success', function(e, data, status, xhr) {
        var modal_to_show = ($(this).data('model-name') === 'contact') ? '#modal-alert-contact' : '#modal-alet-demo';
        return $(modal_to_show).modal('show');
    });

    myform.on('ajax:error', function(e, xhr, status, error) {
        var fields;
        if ($(this).data('model-name') === 'contact') {
            fields = $('.alert-contact-fields');
            if (xhr.responseJSON["email"]) {
                return fields.find('messages').html(makeMessages(xhr.responseJSON)).end().removeClass('hide');
            }
            return fields.addClass('hide');
        } 

        fields = $('.alert-demo-fields');
        if (xhr.responseJSON["company_name"]) {
            return fields.find('.messages').html(makeMessages(xhr.responseJSON)).end().removeClass('hide');
        }
        return fields.addClass('hide');
    });
});

makeMessages is a function that takes the json object and returns a set of strings; map() is a much better function for that than each(), because it requires no intermediate array to save the values.

The 'success' handler shows the use of the 'ternary operator', also known as a conditional expression. You want to know which modal to show: this is how you pick it, then have one and only one 'show' operation. It's way easier to debug.

For the 'error' handler, each time you set the messages, you just call makeMessages() with your JSON and get back the array of strings you want. Because you had to find the messages field inside the alert-*-fields, I call end() which pops the current jquery context back one search (from the 'find' to the initial $() call) and then call 'show' on it instead.

Since you call 'return' at the end of a chosen successful operation, there is no need at all for 'else' statements. They're noise. Either your code does its thing, or it falls through to the next stage.

You could remove my fields = set operations, since performance-wise each will only be called once, so it's harmless to have that repetition. But this makes explicit which region you're working in.

If you want to get insanely dense about what you're working on, put all the decision-making up top (what to work on, what to show), and make the rest of the code pure machinery, the 'how' part of your code. The 'error' handler becomes this:

    myform.on('ajax:error', function(e, xhr, status, error) {
        var handler = (($(this).data('model-name') === 'contact') ? 
                       { 'fieldclass': '.alert-contact-fields', 'objname': 'email' } :
                       { 'fieldclass': '.alert-demo-fields', 'objname': 'company_name' });

        var fields = $(handler.fieldclass);
        if (xhr.responseJSON[handler.objname]) {
            return fields.find('.messages').html(makeMessages(xhr.responseJSON)).end().removeClass('hide');
        }
        return fields.addClass('hide');
    });

At which point 'makeMessages()' just becomes a nice, convenient function because it shows (and names! Good names are always important to maintenance) what you're doing with the JSON object.

One (well, two) last alternatives:

    myform.on('ajax:error', function(e, xhr, status, error) {
        var drawResults = function(fieldclass, objname) {
            var fields = $(fieldclass);
            if (xhr.responseJSON[objname]) {
                return fields.find('messages').html(makeMessages(xhr.responseJSON)).end().removeClass('hide');
            }
            return fields.addClass('hide');
        };

        return ($(this).data('model-name') === 'contact' ?
                drawResults('.alert-contact-fields', 'email') :
                drawResults('.alert-data-fields', 'company_name'));

        /* Absolutely minimal alternative, but some people find
         * using the apply method obfuscating. */

        return drawResults.apply(this, $(this).data('model-name') === 'contact' ?
                                 ['.alert-contact-fields', 'email'] :
                                 ['.alert-data-fields', 'company_name']);
    });

Rather than use fields and decisions up front, this puts all the decision making at the end, and describes what will happen once the decision is made up front. This uses a more familiar syntax of calling a function. It's important to see that drawResults() has access to the xhr object already, so it isn't necessary to pass it in.

One last possible extraction is to turn $(this).data('model-name') === 'contact' into a function, like isDemo() , so that code only happens once and is also well-named.

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