0

I am working on a web application for debtor management and I am refactoring the code and try to adhere to the principle of separation of concerns. But the async nature of AJAX is giving me headaches.

From a jQuery dialog the user can set a flag for a debtor which is then stored in a database. If that succeeds, the dialog shows a notification. Until now I handled everything inside the jQuery Ajax success callback function: validating input, doing the ajax request and updating the content of the dialog.

Of course this lead to spaghetti code.

Thus I created a class AjaxHandler with a static method for setting the flag, which is invoked by the dialog. I thought that the dialog could update itself according the the return value of the AjaxHandler but I did not have the asynchronity in mind.

The following question was helpful in tackling the return values.

How do I return the response from an asynchronous call?

But how can I update the dialog without violating the SoC principle?

EDIT

$("#button").on("click", function() {
    var returnValue = AjaxHandler.setFlag();
    if(returnValue) { $("#div").html("Flag set"); }
    else { $('#div").html("Error setting flag");
});

class AjaxHandler { 
    static setFlag(){
        $.ajax({
            type: "POST",
            url: "ajax/set_flag.php",
            success: function(returndata){
            return returndata; //I know this does not work because of                         
            //ASYNC,but that is not the main point.                                                                        


        }
        }
    })
Rattensau
  • 13
  • 2
  • I think of MVC where you have "controller" that gets an event from the "view" (flag set on UI), and tells the "model" to do something (ajax call in this case). Once the ajax call returns data, the "model" fires an event that your "view" (the dialog) listens to, and when it receives this event, updates the flag on the UI. – Steeve Nov 28 '18 at 11:28

2 Answers2

0

Consider using events perhaps here?

$("#button").on("click", function() {
  $('body').trigger('getdata', ["", $('#div')]);
});
$('body').on('getdata', function(event, datasent, myelement) {
    var attach = event.delegateTarget;// the body here
    var getAjax = $.ajax({
        type: "POST",
        url: "ajax/set_flag.php",
        data: datasent // in case you need to send something
      })
      .done(function(data) {
        $(attach).trigger('gotdata', [data, myelement]);
      });
    getAjax.fail(function() {});

  })
  .on('gotdata', function(event, datathing, myelement) {
    myelement.html(!!datathing ? "Flag set", "Error setting flag");
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

Note that inside those event handlers you could also call some function, pass a namespace for the function, basically do it as you please for your design.

Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100
0

There is many ways to handle async responses, but the jQuery way is slightly different, so when you are already using jQuery, handle it this way:

$('#button').on('click', AjaxHandler.setFlag)

class AjaxHandler {
  static setFlag () {
    this.loading = true
    this
      .asyncReq('ajax/set_flag.php')
      .done(function () {
        $('#div').html('Flag set')
      })
      .fail(function (err) {
        $('#div').html('Error setting flag. Reason: ' + err)
      })
      .always(function () {
        this.loading = false
      })
  }

  asyncReq (url) {
    return $.ajax({
      type: 'POST',
      url: url
    })
  }
})
Vladislav Ladicky
  • 2,261
  • 1
  • 11
  • 13
  • Thank you for pointing me to jQuery deferred objects. That is the route I would like to follow. However, in your example the AjaxHandler controls the contents of the view which I would like to avoid. – Rattensau Nov 29 '18 at 10:49
  • It's ok. Just try to avoid one thing more: sending 200 response from backend, when the operation was not successful. – Vladislav Ladicky Nov 29 '18 at 11:01
  • So, send particular, appropriate 4xx response, so you can react to error in fail() method, not with if() in done() method. – Vladislav Ladicky Nov 29 '18 at 11:11
  • Thanks for the tip but could you please explai, why? Right now the php-backend sends custom JSON error messages that $.post({success: function(returndata)}) was handling. Because there are two types of possible errors: validation errors and sql errors. I am not sure how to allow for that with your approach. And what do you think about using only the always() method instead of done()/fail() and evaluate the server response there? – Rattensau Nov 29 '18 at 11:20
  • See post edit. Always method is there for only operations not dependent on call result, done or fail. Like setting the loading state to off, false. And look for http response codes which will be most appropriate for particular error. – Vladislav Ladicky Nov 29 '18 at 11:31