Philly Dan
Philly Dan

Reputation: 21

Knockout View Instantiating JQuery Datepicker Control onSelect Not Updating DOM Until JS Finishes Executing

I have a page that is created completely using Knockout. In one of the templates, clicking on a link will display a JQuery Datepicker control to select a date. Upon selecting the date, a function executes using the selected date and the Datepicker closes. That much works just fine.

It can take several seconds from when someone selects a date until the Datepicker closes. This is due to a function that is called (LoadAppointmentTimeSlots) which needs to run synchronously and can take a while to do what it does. To address this, I would like a DIV to appear that provides feedback to the user that the system is working ("#loading").

THE PROBLEM is that the DIV does not appear until after the LoadAppointmentTimeSlots function executes (by which time the DIV gets hidden again). I have experimented with setTimeout in several ways, but nothing has worked.

Below is the "offending" code:

var SchedulingViewModel = function () {
  var self = this;
  ...
  self.Date_OnClick = function () {
    var selectedDate;
    $("#calendarPopup").append('<div  id="datepicker" />');
    $("#datepicker").datepicker({
      dateformat: 'mm-dd-yy',
      changeMonth: true,
      changeYear: true,
      setDate: new Date(),
      minDate: 0,
      maxDate: self.SelectedRFVInterval() - 1,
      onSelect: function (datetext, inst) {
        selectedDate = datetext;
        $("#loading").show();
        self.LoadAppointmentTimeSlots(datetext);  // function within view model that                 uses $AJAX in sync mode to return time slot data
        $("#loading").hide();
        $('#calendarPopup').dialog('close');
      }
    });
  };
  ...
}

Upvotes: 2

Views: 353

Answers (1)

CodeThug
CodeThug

Reputation: 3192

The difficulty you are running into is because show() is executed asynchronously, and since javascript is executed in a single thread, that means they have to wait until all synchronous code (such as LoadAppointmentTimeSlots) is done.

To get your desired behaviour, put everything after the show() call into the callback for the show command. That way LoadAppointmentTimeSlots won't execute until the show() call is done. Here is how:

// ... other code
$("#loading").show(function() {
    self.LoadAppointmentTimeSlots(datetext);  
    $("#loading").hide();
    $('#calendarPopup').dialog('close');
});

However, it might be better to change your ajax call in LoadAppointmentTimeSlots to be asynchronous and move the hide() and dialog('close') calls to the callback of the ajax call. This allows javascript to keep doing other things while you are waiting for LoadAppointmentTimeSlots to finish. That might look more like this:

// ... other code
$("#loading").show()
self.LoadAppointmentTimeSlots(datetext, function() {
    $("#loading").hide();
    $('#calendarPopup').dialog('close');
});
// ... more code


function LoadAppointmentTimeSlots(datetext, alwaysCallback) {
    // Prepare request details
    $.ajax( "/myendpoint?param=foo" )
      .done(function(data) { alert("success"); }) // do something with data 
      .fail(function() { alert("error"); })
      .always(alwaysCallback); // called on both success and failure of ajax call
}

Upvotes: 1

Related Questions